/*
* 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) 2009 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.wizard;
import java.awt.Color;
import java.awt.Image;
import java.sql.Blob;
import java.util.ArrayList;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.AbstractReportDefinition;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.Band;
import org.pentaho.reporting.engine.classic.core.CrosstabColumnGroup;
import org.pentaho.reporting.engine.classic.core.CrosstabColumnGroupBody;
import org.pentaho.reporting.engine.classic.core.CrosstabGroup;
import org.pentaho.reporting.engine.classic.core.CrosstabOtherGroup;
import org.pentaho.reporting.engine.classic.core.CrosstabOtherGroupBody;
import org.pentaho.reporting.engine.classic.core.CrosstabRowGroup;
import org.pentaho.reporting.engine.classic.core.CrosstabRowGroupBody;
import org.pentaho.reporting.engine.classic.core.Element;
import org.pentaho.reporting.engine.classic.core.Group;
import org.pentaho.reporting.engine.classic.core.GroupBody;
import org.pentaho.reporting.engine.classic.core.GroupFooter;
import org.pentaho.reporting.engine.classic.core.GroupHeader;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.MetaAttributeNames;
import org.pentaho.reporting.engine.classic.core.RelationalGroup;
import org.pentaho.reporting.engine.classic.core.ReportElement;
import org.pentaho.reporting.engine.classic.core.ReportPreProcessor;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.Section;
import org.pentaho.reporting.engine.classic.core.SubGroupBody;
import org.pentaho.reporting.engine.classic.core.SubReport;
import org.pentaho.reporting.engine.classic.core.Watermark;
import org.pentaho.reporting.engine.classic.core.filter.types.ContentFieldType;
import org.pentaho.reporting.engine.classic.core.filter.types.ContentType;
import org.pentaho.reporting.engine.classic.core.filter.types.DateFieldType;
import org.pentaho.reporting.engine.classic.core.filter.types.LabelType;
import org.pentaho.reporting.engine.classic.core.filter.types.NumberFieldType;
import org.pentaho.reporting.engine.classic.core.filter.types.TextFieldType;
import org.pentaho.reporting.engine.classic.core.function.ProcessingContext;
import org.pentaho.reporting.engine.classic.core.function.StructureFunction;
import org.pentaho.reporting.engine.classic.core.metadata.ElementType;
import org.pentaho.reporting.engine.classic.core.states.datarow.DefaultFlowController;
import org.pentaho.reporting.engine.classic.core.style.BandStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.BorderStyle;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleSheet;
import org.pentaho.reporting.engine.classic.core.wizard.AutoGeneratorUtility;
import org.pentaho.reporting.engine.classic.core.wizard.DataAttributes;
import org.pentaho.reporting.engine.classic.core.wizard.DefaultDataAttributeContext;
import org.pentaho.reporting.engine.classic.core.wizard.RelationalAutoGeneratorPreProcessor;
import org.pentaho.reporting.engine.classic.wizard.model.DetailFieldDefinition;
import org.pentaho.reporting.engine.classic.wizard.model.FieldDefinition;
import org.pentaho.reporting.engine.classic.wizard.model.GroupDefinition;
import org.pentaho.reporting.engine.classic.wizard.model.GroupType;
import org.pentaho.reporting.engine.classic.wizard.model.Length;
import org.pentaho.reporting.engine.classic.wizard.model.RootBandDefinition;
import org.pentaho.reporting.engine.classic.wizard.model.WatermarkDefinition;
import org.pentaho.reporting.engine.classic.wizard.model.WizardSpecification;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
/**
* Todo: Document Me
*
* @author Thomas Morgner
*/
public class WizardProcessor implements ReportPreProcessor
{
private static final Log logger = LogFactory.getLog(WizardProcessor.class);
private DefaultDataAttributeContext attributeContext;
private AbstractReportDefinition definition;
private DefaultFlowController flowController;
private WizardSpecification wizardSpecification;
public WizardProcessor()
{
}
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
public MasterReport performPreProcessing(final MasterReport definition,
final DefaultFlowController flowController)
throws ReportProcessingException
{
return (MasterReport) performCommonPreProcessing(definition, flowController, definition.getResourceManager());
}
public SubReport performPreProcessing(final SubReport definition,
final DefaultFlowController flowController)
throws ReportProcessingException
{
return (SubReport) performCommonPreProcessing(definition, flowController, definition.getResourceManager());
}
protected AbstractReportDefinition performCommonPreProcessing(final AbstractReportDefinition definition,
final DefaultFlowController flowController,
final ResourceManager resourceManager)
throws ReportProcessingException
{
try
{
this.wizardSpecification = WizardProcessorUtil.loadWizardSpecification(definition, resourceManager);
if (wizardSpecification == null)
{
return definition;
}
final StructureFunction[] functions = definition.getStructureFunctions();
boolean hasOverrideFunction = false;
for (int i = 0; i < functions.length; i++)
{
final StructureFunction function = functions[i];
if (function instanceof WizardOverrideFormattingFunction)
{
hasOverrideFunction = true;
break;
}
}
if (hasOverrideFunction == false)
{
definition.addStructureFunction(new WizardOverrideFormattingFunction());
}
final ProcessingContext reportContext = flowController.getReportContext();
this.definition = definition;
this.flowController = flowController;
this.attributeContext = new DefaultDataAttributeContext(reportContext.getOutputProcessorMetaData(),
reportContext.getResourceBundleFactory().getLocale());
final Object o = definition.getAttribute(AttributeNames.Wizard.NAMESPACE, "enable");
if (Boolean.TRUE.equals(o) == false)
{
return performRefreshPreProcessing();
}
else
{
return performGenerationPreProcessing();
}
}
finally
{
this.wizardSpecification = null;
this.definition = null;
this.flowController = null;
this.attributeContext = null;
}
}
private AbstractReportDefinition performRefreshPreProcessing()
throws ReportProcessingException
{
final GroupDefinition[] groupDefinitions = wizardSpecification.getGroupDefinitions();
final int wizardCount = groupDefinitions.length; // 1 5 5
final int groupCount = definition.getGroupCount(); // 5 1 5
final int count = Math.min(groupCount, wizardCount);
for (int i = 0; i < count; i++)
{
final int groupIdx = groupCount - i - 1;
final int wizGroupIdx = wizardCount - i - 1;
final GroupDefinition gd = groupDefinitions[wizGroupIdx];
final Group g = definition.getGroup(groupIdx);
final Band groupHeader = g.getHeader();
final Band groupFooter = g.getFooter();
iterateSection(groupHeader, new UpdateHeaderTask(gd));
iterateSection(groupFooter, new UpdateFooterTask(gd));
}
final Band itemband = definition.getItemBand();
final Band detailsHeader = definition.getDetailsHeader();
final Band detailsFooter = definition.getDetailsFooter();
final DetailFieldDefinition[] detailFieldDefinitions = wizardSpecification.getDetailFieldDefinitions();
for (int i = 0; i < detailFieldDefinitions.length; i++)
{
final DetailFieldDefinition fieldDefinition = detailFieldDefinitions[i];
final UpdateDetailsTask updateTask = new UpdateDetailsTask(fieldDefinition);
iterateSection(itemband, updateTask);
iterateSection(detailsHeader, updateTask);
iterateSection(detailsFooter, updateTask);
}
return definition;
}
private static interface UpdateTask
{
public void processElement(ReportElement e);
}
private static class UpdateHeaderTask implements UpdateTask
{
private GroupDefinition groupDefinition;
private UpdateHeaderTask(final GroupDefinition groupDefinition)
{
this.groupDefinition = groupDefinition;
}
public void processElement(final ReportElement e)
{
if (Boolean.TRUE.equals
(e.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING)))
{
e.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFormatData", groupDefinition.getHeader());
}
if (Boolean.TRUE.equals
(e.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES)))
{
e.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFieldData", groupDefinition);
}
}
}
private static class UpdateFooterTask implements UpdateTask
{
private GroupDefinition groupDefinition;
private UpdateFooterTask(final GroupDefinition groupDefinition)
{
this.groupDefinition = groupDefinition;
}
public void processElement(final ReportElement e)
{
if (Boolean.TRUE.equals
(e.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING)))
{
e.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFormatData", groupDefinition.getFooter());
}
if (Boolean.TRUE.equals
(e.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES)))
{
e.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFieldData", groupDefinition);
}
}
}
private static class UpdateDetailsTask implements UpdateTask
{
private FieldDefinition fieldDefinition;
private UpdateDetailsTask(final FieldDefinition groupDefinition)
{
this.fieldDefinition = groupDefinition;
}
public void processElement(final ReportElement e)
{
final String field = fieldDefinition.getField();
final Object labelFor = e.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.LABEL_FOR);
if (labelFor != null)
{
if (ObjectUtilities.equal(field, labelFor) == false)
{
return;
}
}
else
{
final Object fieldName = e.getAttribute(AttributeNames.Core.NAMESPACE, AttributeNames.Core.FIELD);
if (ObjectUtilities.equal(field, fieldName) == false)
{
return;
}
}
if (Boolean.TRUE.equals
(e.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING)))
{
e.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFormatData", fieldDefinition);
}
if (Boolean.TRUE.equals
(e.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES)))
{
e.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFieldData", fieldDefinition);
}
}
}
private void iterateSection(final Section s, final UpdateTask task)
{
final int count = s.getElementCount();
for (int i = 0; i < count; i++)
{
final ReportElement element = s.getElement(i);
task.processElement(element);
if (element instanceof SubReport)
{
continue;
}
if (element instanceof Section)
{
iterateSection((Section) element, task);
}
}
}
protected AbstractReportDefinition performGenerationPreProcessing()
throws ReportProcessingException
{
setupWatermark();
setupRelationalGroups();
/** remove check on data source to display to use cross-tab
if (isCrosstab())
{
setupCrosstab();
}
else
{
clearCrosstab();
}
*/
setupDetails();
return definition;
}
private void clearCrosstab()
{
final CrosstabGroup crosstabGroup = lookupCrosstab();
if (crosstabGroup != null)
{
if (definition.getRootGroup() == crosstabGroup)
{
definition.setRootGroup(new RelationalGroup());
}
else
{
final Section parentSection = crosstabGroup.getParentSection();
if (parentSection instanceof SubGroupBody)
{
final SubGroupBody sgb = (SubGroupBody) parentSection;
sgb.setGroup(new RelationalGroup());
}
}
}
}
/**
* Creating a crosstab report deletes all sub-definitions and the itemband.
*
* @throws ReportProcessingException if an error occurs.
*/
private void setupCrosstab() throws ReportProcessingException
{
CrosstabGroup crosstab = lookupCrosstab();
if (crosstab == null)
{
crosstab = new CrosstabGroup();
insertCrosstab(crosstab);
}
Group insertGroup = crosstab;
final GroupDefinition[] groupDefinitions = wizardSpecification.getGroupDefinitions();
for (int i = 0; i < groupDefinitions.length; i++)
{
final GroupDefinition groupDefinition = groupDefinitions[i];
if (GroupType.CT_OTHER.equals(groupDefinition.getGroupType()) == false)
{
continue;
}
// create a new group and insert it at the end
final CrosstabOtherGroup relationalGroup = new CrosstabOtherGroup();
if (groupDefinition.getGroupName() != null)
{
relationalGroup.setName(groupDefinition.getGroupName());
}
configureCrosstabOtherGroup(relationalGroup, groupDefinition);
insertGroup.setBody(new CrosstabOtherGroupBody(relationalGroup));
insertGroup = relationalGroup;
}
for (int i = 0; i < groupDefinitions.length; i++)
{
final GroupDefinition groupDefinition = groupDefinitions[i];
if (GroupType.CT_ROW.equals(groupDefinition.getGroupType()) == false)
{
continue;
}
// create a new group and insert it at the end
final CrosstabRowGroup relationalGroup = new CrosstabRowGroup();
if (groupDefinition.getGroupName() != null)
{
relationalGroup.setName(groupDefinition.getGroupName());
}
configureCrosstabRowGroup(relationalGroup, groupDefinition);
insertGroup.setBody(new CrosstabRowGroupBody(relationalGroup));
insertGroup = relationalGroup;
}
for (int i = 0; i < groupDefinitions.length; i++)
{
final GroupDefinition groupDefinition = groupDefinitions[i];
if (GroupType.CT_COLUMN.equals(groupDefinition.getGroupType()) == false)
{
continue;
}
// create a new group and insert it at the end
final CrosstabColumnGroup relationalGroup = new CrosstabColumnGroup();
if (groupDefinition.getGroupName() != null)
{
relationalGroup.setName(groupDefinition.getGroupName());
}
configureCrosstabColumnGroup(relationalGroup, groupDefinition);
insertGroup.setBody(new CrosstabColumnGroupBody(relationalGroup));
insertGroup = relationalGroup;
}
final Band itemBand = AutoGeneratorUtility.findGeneratedContent(definition.getItemBand());
if (itemBand == null)
{
return;
}
final DetailFieldDefinition[] detailFieldDefinitions = wizardSpecification.getDetailFieldDefinitions();
if (detailFieldDefinitions.length == 0)
{
return;
}
final Float[] widthSpecs = new Float[detailFieldDefinitions.length];
for (int i = 0; i < detailFieldDefinitions.length; i++)
{
final DetailFieldDefinition fieldDefinition = detailFieldDefinitions[i];
final Length length = fieldDefinition.getWidth();
if (length == null)
{
continue;
}
widthSpecs[i] = length.getNormalizedValue();
}
final float[] computedWidth =
AutoGeneratorUtility.computeFieldWidths(widthSpecs, definition.getPageDefinition().getWidth());
itemBand.getStyle().setStyleProperty(BandStyleKeys.LAYOUT, "row");
for (int i = 0; i < detailFieldDefinitions.length; i++)
{
final DetailFieldDefinition detailFieldDefinition = detailFieldDefinitions[i];
setupField(null, null, itemBand, detailFieldDefinition, computedWidth[i]);
}
}
private void setupDetails() throws ReportProcessingException
{
final DetailFieldDefinition[] detailFieldDefinitions = wizardSpecification.getDetailFieldDefinitions();
if (detailFieldDefinitions.length == 0)
{
if (wizardSpecification.isAutoGenerateDetails())
{
final RelationalAutoGeneratorPreProcessor generatorPreProcessor = new RelationalAutoGeneratorPreProcessor();
if (definition instanceof MasterReport)
{
generatorPreProcessor.performPreProcessing((MasterReport) definition, flowController);
}
else if (definition instanceof SubReport)
{
generatorPreProcessor.performPreProcessing((SubReport) definition, flowController);
}
}
return;
}
definition.getDetailsHeader().setRepeat(true);
definition.getDetailsFooter().setRepeat(true);
final Band detailsHeader = AutoGeneratorUtility.findGeneratedContent(definition.getDetailsHeader());
final Band detailsFooter = AutoGeneratorUtility.findGeneratedContent(definition.getDetailsFooter());
final Band itemBand = AutoGeneratorUtility.findGeneratedContent(definition.getItemBand());
if (itemBand == null)
{
return;
}
final Float[] widthSpecs = new Float[detailFieldDefinitions.length];
for (int i = 0; i < detailFieldDefinitions.length; i++)
{
final DetailFieldDefinition fieldDefinition = detailFieldDefinitions[i];
final Length length = fieldDefinition.getWidth();
if (length == null)
{
continue;
}
widthSpecs[i] = length.getNormalizedValue();
}
final float[] computedWidth =
AutoGeneratorUtility.computeFieldWidths(widthSpecs, definition.getPageDefinition().getWidth());
itemBand.getStyle().setStyleProperty(BandStyleKeys.LAYOUT, "row");
if (detailsHeader != null)
{
detailsHeader.getStyle().setStyleProperty(BandStyleKeys.LAYOUT, "row");
}
if (detailsFooter != null)
{
detailsFooter.getStyle().setStyleProperty(BandStyleKeys.LAYOUT, "row");
}
for (int i = 0; i < detailFieldDefinitions.length; i++)
{
final DetailFieldDefinition detailFieldDefinition = detailFieldDefinitions[i];
setupField(detailsHeader, detailsFooter, itemBand, detailFieldDefinition, computedWidth[i]);
}
if (detailsFooter != null)
{
final Element[] elements = detailsFooter.getElementArray();
boolean footerEmpty = true;
for (int i = 0; i < elements.length; i++)
{
final Element element = elements[i];
if ("label".equals(element.getElementTypeName()) == false)
{
footerEmpty = false;
break;
}
}
if (footerEmpty)
{
detailsFooter.clear();
}
}
}
private void setupDefaultPadding(final Band band, final Element detailElement)
{
final Object maybePaddingTop = band.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.PADDING_TOP);
final Object maybePaddingLeft = band.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.PADDING_LEFT);
final Object maybePaddingBottom = band.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.PADDING_BOTTOM);
final Object maybePaddingRight = band.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.PADDING_RIGHT);
if (maybePaddingTop instanceof Number == false ||
maybePaddingLeft instanceof Number == false ||
maybePaddingBottom instanceof Number == false ||
maybePaddingRight instanceof Number == false)
{
return;
}
final Number paddingTop = (Number) maybePaddingTop;
final Number paddingLeft = (Number) maybePaddingLeft;
final Number paddingBottom = (Number) maybePaddingBottom;
final Number paddingRight = (Number) maybePaddingRight;
final ElementStyleSheet styleSheet = detailElement.getStyle();
styleSheet.setStyleProperty(ElementStyleKeys.PADDING_TOP, new Float(paddingTop.floatValue()));
styleSheet.setStyleProperty(ElementStyleKeys.PADDING_LEFT, new Float(paddingLeft.floatValue()));
styleSheet.setStyleProperty(ElementStyleKeys.PADDING_BOTTOM, new Float(paddingBottom.floatValue()));
styleSheet.setStyleProperty(ElementStyleKeys.PADDING_RIGHT, new Float(paddingRight.floatValue()));
}
private void setupDefaultGrid(final Band band, final Element detailElement)
{
setupDefaultPadding(band, detailElement);
final ElementStyleSheet styleSheet = detailElement.getStyle();
// Always make the height of the detailElement dynamic to the band
// According to thomas negative numbers equate to percentages
styleSheet.setStyleProperty(ElementStyleKeys.MIN_HEIGHT, new Float(-100));
final Object maybeBorderStyle = band.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.GRID_STYLE);
final Object maybeBorderWidth = band.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.GRID_WIDTH);
final Object maybeBorderColor = band.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.GRID_COLOR);
if (maybeBorderColor instanceof Color == false ||
maybeBorderStyle instanceof BorderStyle == false ||
maybeBorderWidth instanceof Number == false)
{
return;
}
final BorderStyle style = (BorderStyle) maybeBorderStyle;
final Color color = (Color) maybeBorderColor;
final Number number = (Number) maybeBorderWidth;
final Float width = new Float(number.floatValue());
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_TOP_WIDTH, width);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_TOP_COLOR, color);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_TOP_STYLE, style);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_LEFT_WIDTH, width);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_LEFT_COLOR, color);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_LEFT_STYLE, style);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_BOTTOM_WIDTH, width);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_BOTTOM_COLOR, color);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_BOTTOM_STYLE, style);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_RIGHT_WIDTH, width);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_RIGHT_COLOR, color);
styleSheet.setStyleProperty(ElementStyleKeys.BORDER_RIGHT_STYLE, style);
}
private void setupField(final Band detailsHeader,
final Band detailsFooter,
final Band itemBand,
final DetailFieldDefinition field,
final float width) throws ReportProcessingException
{
if (StringUtils.isEmpty(field.getField()))
{
return;
}
final Element detailElement =
AutoGeneratorUtility.generateDetailsElement(field.getField(), computeElementType(field));
setupDefaultGrid(itemBand, detailElement);
final String id = "wizard::details-" + field.getField();
detailElement.setName(id);
detailElement.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, new Float(width));
if (Boolean.TRUE.equals
(detailElement.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING)))
{
detailElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFormatData", field);
}
if (Boolean.TRUE.equals
(detailElement.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES)))
{
detailElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFieldData", field);
}
itemBand.addElement(detailElement);
if (Boolean.TRUE.equals(field.getOnlyShowChangingValues()))
{
detailElement.setAttribute
(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ONLY_SHOW_CHANGING_VALUES, Boolean.TRUE);
}
if (detailsHeader != null)
{
final Element headerElement = AutoGeneratorUtility.generateHeaderElement(field.getField());
setupDefaultGrid(detailsHeader, headerElement);
headerElement.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, new Float(width));
if (Boolean.TRUE.equals
(headerElement.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING)))
{
headerElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFormatData", field);
}
if (Boolean.TRUE.equals
(headerElement.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES)))
{
headerElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFieldData", field);
}
headerElement.setAttribute(AttributeNames.Wizard.NAMESPACE,
MetaAttributeNames.Style.HORIZONTAL_ALIGNMENT, field.getHorizontalAlignment());
detailsHeader.addElement(headerElement);
}
if (detailsFooter != null)
{
final Class aggFunctionClass = field.getAggregationFunction();
final Element footerElement = AutoGeneratorUtility.generateFooterElement
(aggFunctionClass, computeElementType(field), null, field.getField());
setupDefaultGrid(detailsFooter, footerElement);
footerElement.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, new Float(width));
if (Boolean.TRUE.equals
(footerElement.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING)))
{
footerElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFormatData", field);
}
if (Boolean.TRUE.equals
(footerElement.getAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES)))
{
footerElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFieldData", field);
}
detailsFooter.addElement(footerElement);
}
}
/**
* Removes the unusedTemplateGroups based on the assumption that if a group doesn't have any
* fields assigned to it that it is empty.
*/
private void removedUnusedTemplateGroups(final int groupsDefined)
{
final RelationalGroup[] templateRelationalGroups = getTemplateRelationalGroups();
final int templateRelationalGroupCount = templateRelationalGroups.length;
for (int i = groupsDefined; i < templateRelationalGroupCount; i++)
{
RelationalGroup templateRelationalGroup = templateRelationalGroups[i];
definition.removeGroup(templateRelationalGroup);
}
}
/**
* @return the relational groups in the templates in a flattened array.
*/
private RelationalGroup[] getTemplateRelationalGroups()
{
ArrayList relationalGroups = new ArrayList();
Group group = definition.getRootGroup();
while (group != null && group instanceof RelationalGroup)
{
relationalGroups.add(group);
final GroupBody body = group.getBody();
if (body instanceof SubGroupBody)
{
final SubGroupBody sgBody = (SubGroupBody) body;
if (sgBody.getGroup() instanceof RelationalGroup)
{
group = sgBody.getGroup();
}
else
{
group = null;
}
}
else
{
group = null;
}
}
return (RelationalGroup[]) relationalGroups.toArray(new RelationalGroup[relationalGroups.size()]);
}
private void setupRelationalGroups() throws ReportProcessingException
{
final Group rootgroup = definition.getRootGroup();
RelationalGroup group;
if (rootgroup instanceof RelationalGroup == false)
{
group = null;
}
else
{
group = (RelationalGroup) rootgroup;
}
final RelationalGroup template = findInnermostRelationalGroup(definition);
final GroupDefinition[] groupDefinitions = wizardSpecification.getGroupDefinitions();
for (int i = 0; i < groupDefinitions.length; i++)
{
final GroupDefinition groupDefinition = groupDefinitions[i];
final GroupType type = groupDefinition.getGroupType();
if (type != null && GroupType.RELATIONAL.equals(type) == false)
{
continue;
}
if (group == null)
{
// create a new group and insert it at the end
RelationalGroup relationalGroup;
if (template != null)
{
try
{
relationalGroup = (RelationalGroup) template.derive();
}
catch (CloneNotSupportedException e)
{
relationalGroup = new RelationalGroup();
}
}
else
{
relationalGroup = new RelationalGroup();
}
if (groupDefinition.getGroupName() != null)
{
relationalGroup.setName(groupDefinition.getGroupName());
}
configureRelationalGroup(relationalGroup, groupDefinition);
insertGroup(relationalGroup);
}
else
{
// modify the existing group
configureRelationalGroup(group, groupDefinition);
final GroupBody body = group.getBody();
if (body instanceof SubGroupBody)
{
final SubGroupBody sgBody = (SubGroupBody) body;
if (sgBody.getGroup() instanceof RelationalGroup)
{
group = (RelationalGroup) sgBody.getGroup();
}
else
{
group = null;
}
}
else
{
group = null;
}
}
}
// Remove any group bands are not being used ie. groups with no fields
removedUnusedTemplateGroups(groupDefinitions.length);
}
private RelationalGroup findInnermostRelationalGroup(final AbstractReportDefinition definition)
{
RelationalGroup retval = null;
Group existingGroup = definition.getRootGroup();
while (existingGroup instanceof RelationalGroup)
{
retval = (RelationalGroup) existingGroup;
final GroupBody body = existingGroup.getBody();
if (body instanceof SubGroupBody == false)
{
return retval;
}
final SubGroupBody sgb = (SubGroupBody) body;
existingGroup = sgb.getGroup();
}
return retval;
}
/**
* Inserts the crosstab into the report as innermost group. This method will fail if there is already a crosstab
* active.
*
* @param crosstabGroup
*/
private void insertCrosstab(final CrosstabGroup crosstabGroup)
{
Group existingGroup = definition.getRootGroup();
GroupBody gb = existingGroup.getBody();
while (gb instanceof SubGroupBody)
{
final SubGroupBody sgb = (SubGroupBody) gb;
existingGroup = sgb.getGroup();
gb = existingGroup.getBody();
}
existingGroup.setBody(new SubGroupBody(crosstabGroup));
}
private CrosstabGroup lookupCrosstab()
{
Group existingGroup = definition.getRootGroup();
if (existingGroup instanceof CrosstabGroup)
{
return (CrosstabGroup) existingGroup;
}
GroupBody gb = existingGroup.getBody();
while (gb instanceof SubGroupBody)
{
final SubGroupBody sgb = (SubGroupBody) gb;
existingGroup = sgb.getGroup();
if (existingGroup instanceof CrosstabGroup)
{
return (CrosstabGroup) existingGroup;
}
gb = existingGroup.getBody();
}
return null;
}
private void insertGroup(final RelationalGroup group)
{
Group lastGroup = null;
Group insertGroup = definition.getRootGroup();
while (true)
{
if (insertGroup instanceof RelationalGroup == false)
{
if (lastGroup == null)
{
definition.setRootGroup(group);
group.setBody(new SubGroupBody(insertGroup));
return;
}
final GroupBody body = lastGroup.getBody();
final SubGroupBody sgb = new SubGroupBody(group);
lastGroup.setBody(sgb);
group.setBody(body);
return;
}
final GroupBody body = insertGroup.getBody();
if (body instanceof SubGroupBody == false)
{
final SubGroupBody sgb = new SubGroupBody(group);
insertGroup.setBody(sgb);
group.setBody(body);
return;
}
lastGroup = insertGroup;
final SubGroupBody sgb = (SubGroupBody) body;
insertGroup = sgb.getGroup();
}
}
private void configureCrosstabOtherGroup(final CrosstabOtherGroup group,
final GroupDefinition groupDefinition) throws ReportProcessingException
{
final String groupField = groupDefinition.getField();
group.setField(groupField);
configureCrosstabGroupHeader(group, groupDefinition);
configureCrosstabGroupFooter(group, groupDefinition);
}
private void configureCrosstabRowGroup(final CrosstabRowGroup group,
final GroupDefinition groupDefinition) throws ReportProcessingException
{
final String groupField = groupDefinition.getField();
group.setField(groupField);
configureCrosstabGroupHeader(group, groupDefinition);
configureCrosstabGroupFooter(group, groupDefinition);
}
private void configureCrosstabColumnGroup(final CrosstabColumnGroup group,
final GroupDefinition groupDefinition) throws ReportProcessingException
{
final String groupField = groupDefinition.getField();
group.setField(groupField);
configureCrosstabGroupHeader(group, groupDefinition);
configureCrosstabGroupFooter(group, groupDefinition);
}
private void configureRelationalGroup(final RelationalGroup group,
final GroupDefinition groupDefinition) throws ReportProcessingException
{
final String groupField = groupDefinition.getField();
if (groupField != null)
{
group.setFieldsArray(new String[]{groupField});
}
configureRelationalGroupFooter(group, groupDefinition);
configureRelationalGroupHeader(group, groupDefinition);
}
private void configureCrosstabGroupHeader(final Group group,
final GroupDefinition groupDefinition)
{
final RootBandDefinition headerDefinition = groupDefinition.getHeader();
if (headerDefinition.isVisible())
{
final GroupHeader header = group.getHeader();
final Boolean repeat = headerDefinition.getRepeat();
if (repeat != null)
{
header.setRepeat(repeat.booleanValue());
}
final Band content = AutoGeneratorUtility.findGeneratedContent(header);
if (content == null)
{
return;
}
final Element headerElement = AutoGeneratorUtility.generateDetailsElement
(groupDefinition.getField(), computeElementType(groupDefinition));
final Length length = groupDefinition.getWidth();
if (length != null)
{
headerElement.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, length.getNormalizedValue());
}
headerElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFormatData", headerDefinition);
headerElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFieldData", groupDefinition);
content.clear();
content.addElement(headerElement);
}
}
private void configureRelationalGroupHeader(final Group group,
final GroupDefinition groupDefinition)
{
final RootBandDefinition headerDefinition = groupDefinition.getHeader();
if (headerDefinition.isVisible())
{
final GroupHeader header = group.getHeader();
final Boolean repeat = headerDefinition.getRepeat();
if (repeat != null)
{
header.setRepeat(repeat.booleanValue());
}
final Band content = AutoGeneratorUtility.findGeneratedContent(header);
if (content == null)
{
return;
}
final Element headerLabelElement = new Element();
headerLabelElement.setElementType(new LabelType());
if (groupDefinition.getDisplayName() != null)
{
headerLabelElement.setAttribute
(AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, groupDefinition.getDisplayName());
}
else
{
headerLabelElement.setAttribute
(AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, groupDefinition.getField());
headerLabelElement.setAttribute
(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.LABEL_FOR, groupDefinition.getField());
headerLabelElement.setAttribute(AttributeNames.Wizard.NAMESPACE,
AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES, Boolean.TRUE);
}
final Element headerValueElement =
AutoGeneratorUtility.generateDetailsElement(groupDefinition.getField(), computeElementType(groupDefinition));
headerValueElement.setAttribute(AttributeNames.Core.NAMESPACE, AttributeNames.Core.NULL_VALUE, "-");
final Band headerElement = new Band();
headerElement.getStyle().setStyleProperty(BandStyleKeys.LAYOUT, "inline");
headerElement.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, new Float(-100));
headerElement.getStyle().setStyleProperty(ElementStyleKeys.DYNAMIC_HEIGHT, Boolean.TRUE);
headerElement.setAttribute(AttributeNames.Wizard.NAMESPACE,
AttributeNames.Wizard.ALLOW_METADATA_STYLING, Boolean.TRUE);
headerElement.setAttribute
(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.LABEL_FOR, groupDefinition.getField());
headerElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFormatData", headerDefinition);
headerElement.addElement(headerLabelElement);
headerElement.addElement(headerValueElement);
content.clear();
content.addElement(headerElement);
}
}
private void configureRelationalGroupFooter(final Group group, final GroupDefinition groupDefinition)
throws ReportProcessingException
{
final RootBandDefinition footerDefinition = groupDefinition.getFooter();
if (footerDefinition.isVisible() == false)
{
return;
}
if (groupDefinition.getAggregationFunction() == null && (groupDefinition.getGroupTotalsLabel() == null || groupDefinition.getGroupTotalsLabel().length() == 0))
{
return;
}
final GroupFooter footer = group.getFooter();
final Boolean repeat = footerDefinition.getRepeat();
if (repeat != null)
{
footer.setRepeat(repeat.booleanValue());
}
final Band content = AutoGeneratorUtility.findGeneratedContent(footer);
if (content == null)
{
return;
}
final Class aggFunctionClass = groupDefinition.getAggregationFunction();
final Element footerValueElement = AutoGeneratorUtility.generateFooterElement
(aggFunctionClass, computeElementType(groupDefinition),
groupDefinition.getGroupName(), groupDefinition.getField());
final Element footerLabelElement = new Element();
footerLabelElement.setElementType(new LabelType());
if (groupDefinition.getGroupTotalsLabel() != null)
{
footerLabelElement.setAttribute
(AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, groupDefinition.getGroupTotalsLabel());
}
else
{
footerLabelElement.setAttribute
(AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, groupDefinition.getField());
footerLabelElement.setAttribute
(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.LABEL_FOR, groupDefinition.getField());
footerLabelElement.setAttribute(AttributeNames.Wizard.NAMESPACE,
AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES, Boolean.TRUE);
}
final Band footerElement = new Band();
footerElement.getStyle().setStyleProperty(BandStyleKeys.LAYOUT, "inline");
footerElement.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, new Float(-100));
footerElement.getStyle().setStyleProperty(ElementStyleKeys.DYNAMIC_HEIGHT, Boolean.TRUE);
footerElement.setAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING,
Boolean.TRUE);
footerElement.setAttribute
(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.LABEL_FOR, groupDefinition.getField());
footerElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFormatData", footerDefinition);
footerElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFieldData", groupDefinition);
footerElement.addElement(footerLabelElement);
footerElement.addElement(footerValueElement);
content.clear();
content.addElement(footerElement);
}
private void configureCrosstabGroupFooter(final Group group, final GroupDefinition groupDefinition)
throws ReportProcessingException
{
final RootBandDefinition footerDefinition = groupDefinition.getFooter();
if (footerDefinition.isVisible() == false)
{
return;
}
if (groupDefinition.getAggregationFunction() == null)
{
return;
}
final GroupFooter footer = group.getFooter();
final Boolean repeat = footerDefinition.getRepeat();
if (repeat != null)
{
footer.setRepeat(repeat.booleanValue());
}
final Band content = AutoGeneratorUtility.findGeneratedContent(footer);
if (content == null)
{
return;
}
final Class aggFunctionClass = groupDefinition.getAggregationFunction();
final Element footerValueElement = AutoGeneratorUtility.generateFooterElement
(aggFunctionClass, computeElementType(groupDefinition),
groupDefinition.getGroupName(), groupDefinition.getField());
footerValueElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFormatData", footer);
footerValueElement.setAttribute(AttributeNames.Wizard.NAMESPACE, "CachedWizardFieldData", groupDefinition);
content.clear();
content.addElement(footerValueElement);
}
private ElementType computeElementType(final FieldDefinition fieldDefinition)
{
// todo: Make this configurable or even better: drop it into the element metadata.
final String field = fieldDefinition.getField();
final DataAttributes attributes = flowController.getDataSchema().getAttributes(field);
if (attributes == null)
{
logger.warn("Field '" + field + "' is declared in the wizard-specification, " +
"but not present in the data. Assuming defaults.");
return new TextFieldType();
}
final Class fieldType = (Class) attributes.getMetaAttribute
(MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.TYPE, Class.class, attributeContext);
if (fieldType == null)
{
return new TextFieldType();
}
if (Number.class.isAssignableFrom(fieldType))
{
return new NumberFieldType();
}
if (Date.class.isAssignableFrom(fieldType))
{
return new DateFieldType();
}
if (byte[].class.isAssignableFrom(fieldType) ||
Blob.class.isAssignableFrom(fieldType) ||
Image.class.isAssignableFrom(fieldType))
{
return new ContentFieldType();
}
return new TextFieldType();
}
private boolean isCrosstab()
{
final GroupDefinition[] groupDefinitions = wizardSpecification.getGroupDefinitions();
for (int i = 0; i < groupDefinitions.length; i++)
{
final GroupDefinition groupDefinition = groupDefinitions[i];
final GroupType groupType = groupDefinition.getGroupType();
if (GroupType.CT_COLUMN.equals(groupType) ||
GroupType.CT_ROW.equals(groupType))
{
return true;
}
}
final DataAttributes tableAttributes = flowController.getDataSchema().getTableAttributes();
final Object crosstabMode = tableAttributes.getMetaAttribute
(MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.CROSSTAB_MODE, String.class, attributeContext);
if (MetaAttributeNames.Core.CROSSTAB_VALUE_NORMALIZED.equals(crosstabMode))
{
// the data-source indicates a high probability that a crosstab is requested.
return true;
}
return false;
}
private void setupWatermark()
{
final WatermarkDefinition watermarkDefinition = wizardSpecification.getWatermarkDefinition();
if (watermarkDefinition.isVisible() == false)
{
return;
}
if (watermarkDefinition.getSource() == null)
{
return;
}
final Watermark watermark = definition.getWatermark();
final Band content = AutoGeneratorUtility.findGeneratedContent(watermark);
if (content == null)
{
// there is already some content, and we are not allowed to override it.
return;
}
content.clear();
final Element watermarkImage = new Element();
watermarkImage.setElementType(new ContentType());
watermarkImage.setAttribute
(AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, watermarkDefinition.getSource());
final ElementStyleSheet watermarkStyle = watermarkImage.getStyle();
watermarkStyle.setStyleProperty(ElementStyleKeys.POS_X, convertLength(watermarkDefinition.getX()));
watermarkStyle.setStyleProperty(ElementStyleKeys.POS_Y, convertLength(watermarkDefinition.getY()));
watermarkStyle.setStyleProperty(ElementStyleKeys.MIN_WIDTH, convertLength(watermarkDefinition.getWidth()));
watermarkStyle.setStyleProperty(ElementStyleKeys.MIN_HEIGHT, convertLength(watermarkDefinition.getHeight()));
watermarkStyle.setStyleProperty(ElementStyleKeys.KEEP_ASPECT_RATIO, watermarkDefinition.getKeepAspectRatio());
watermarkStyle.setStyleProperty(ElementStyleKeys.SCALE, watermarkDefinition.getScale());
content.addElement(watermarkImage);
}
private Float convertLength(final Length length)
{
if (length == null)
{
return null;
}
return length.getNormalizedValue();
}
}