blob: 2afda831ecc8771cf7aca16acecb2769a972b931 [file] [log] [blame]
/**************************************************************
*
* 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 com.sun.star.report.pentaho.layoutprocessor;
import com.sun.star.report.pentaho.model.OfficeGroupSection;
import com.sun.star.report.pentaho.model.ReportElement;
import org.jfree.report.DataFlags;
import org.jfree.report.DataRow;
import org.jfree.report.DataSourceException;
import org.jfree.report.ReportDataFactoryException;
import org.jfree.report.ReportProcessingException;
import org.jfree.report.expressions.Expression;
import org.jfree.report.flow.FlowController;
import org.jfree.report.flow.ReportTarget;
import org.jfree.report.flow.layoutprocessor.AbstractLayoutController;
import org.jfree.report.flow.layoutprocessor.LayoutController;
import org.jfree.report.flow.layoutprocessor.LayoutControllerUtil;
import org.jfree.report.flow.layoutprocessor.SectionLayoutController;
import org.jfree.report.structure.Element;
import org.jfree.report.structure.Group;
import org.pentaho.reporting.libraries.formula.lvalues.ContextLookup;
import org.pentaho.reporting.libraries.formula.lvalues.LValue;
/**
* Todo: Document me!
*
* @author Thomas Morgner
* @since 05.03.2007
* @noinspection CloneableClassWithoutClone
*/
public abstract class AbstractReportElementLayoutController
extends AbstractLayoutController
{
public static final int NOT_STARTED = 0;
public static final int FINISHED = 2;
private int state;
protected AbstractReportElementLayoutController()
{
}
/**
* Advances the processing position.
*
* @param target the report target that receives generated events.
* @return the new layout controller instance representing the new state.
*
* @throws org.jfree.report.DataSourceException if there was a problem reading data from
* the datasource.
* @throws org.jfree.report.ReportProcessingException if there was a general problem during
* the report processing.
* @throws org.jfree.report.ReportDataFactoryException if a query failed.
*/
public LayoutController advance(final ReportTarget target)
throws DataSourceException, ReportDataFactoryException,
ReportProcessingException
{
if (state != AbstractReportElementLayoutController.NOT_STARTED)
{
throw new IllegalStateException();
}
boolean isPrintableContent = true;
final ReportElement text = (ReportElement) getNode();
// Tests we have to perform:
// 1. Print when group changes. We can know whether a group changed by
// looking at the newly introduced iteration counter.
//
// Whether we use the next one or the one after that depends on whether
// this element is a child of a group-header or group-footer.
// 2. Print repeated values. This never applies to static text or static
// elements.
if ((text.isPrintWhenGroupChanges() && !isGroupChanged()) || (!text.isPrintRepeatedValues() && !isValueChanged()))
{
// if this is set to true, then we print the element only if this is the
// first occurrence in this group.
// or
// If this is set to true, we evaluate the formula of the element and
// try to derive whether there was a change.
isPrintableContent = false;
}
// 3. Evaluate the Display Condition
final Expression dc = text.getDisplayCondition();
if (dc != null)
{
final Object o = LayoutControllerUtil.evaluateExpression(getFlowController(), text, dc);
if (Boolean.FALSE.equals(o))
{
// LOGGER.debug ("DISPLAY Condition forbids printing");
isPrintableContent = false;
}
}
if (!isPrintableContent)
{
// There is no printable content at all. Set the state to FINISHED
return join(getFlowController());
}
else
{
// delegate to the handler ..
return delegateContentGeneration(target);
}
}
protected abstract boolean isValueChanged();
protected boolean isGroupChanged()
{
// search the group.
final SectionLayoutController slc = findGroup();
if (slc == null)
{
// Always print the content of the report header and footer and
// the page header and footer.
return true;
}
// we are in the first iteration, so yes, the group has changed recently.
return slc.getIterationCount() == 0;
}
private SectionLayoutController findGroup()
{
LayoutController parent = getParent();
boolean skipNext = false;
while (parent != null)
{
if (!(parent instanceof SectionLayoutController))
{
parent = parent.getParent();
}
else
{
final SectionLayoutController slc = (SectionLayoutController) parent;
final Element element = slc.getElement();
if (element instanceof OfficeGroupSection)
{
// This is a header or footer. So we take the next group instead.
skipNext = true;
parent = parent.getParent();
}
else if (!(element instanceof Group))
{
parent = parent.getParent();
}
else if (skipNext)
{
skipNext = false;
parent = parent.getParent();
}
else
{
return (SectionLayoutController) parent;
}
}
}
return null;
}
/**
* Joins with a delegated process flow. This is generally called from a child
* flow and should *not* (I mean it!) be called from outside. If you do,
* you'll suffer.
*
* @param flowController the flow controller of the parent.
* @return the joined layout controller that incorperates all changes from the
* delegate.
*/
public LayoutController join(final FlowController flowController)
throws DataSourceException, ReportDataFactoryException,
ReportProcessingException
{
final AbstractReportElementLayoutController alc =
(AbstractReportElementLayoutController) clone();
alc.state = AbstractReportElementLayoutController.FINISHED;
return alc;
}
protected abstract LayoutController delegateContentGeneration(final ReportTarget target)
throws ReportProcessingException, ReportDataFactoryException,
DataSourceException;
/**
* Checks, whether the layout controller would be advanceable. If this method
* returns true, it is generally safe to call the 'advance()' method.
*
* @return true, if the layout controller is advanceable, false otherwise.
*/
public boolean isAdvanceable()
{
return state != AbstractReportElementLayoutController.FINISHED;
}
protected boolean isReferenceChanged(final LValue lValue)
{
if (lValue instanceof ContextLookup)
{
final ContextLookup rval = (ContextLookup) lValue;
final String s = rval.getName();
final DataRow view = getFlowController().getMasterRow().getGlobalView();
try
{
final DataFlags flags = view.getFlags(s);
if (flags != null && flags.isChanged())
{
// LOGGER.debug ("Reference " + s + " is changed");
return true;
}
// LOGGER.debug ("Reference " + s + " is unchanged");
}
catch (DataSourceException e)
{
// ignore .. assume that the reference has not changed.
}
}
final LValue[] childValues = lValue.getChildValues();
for (int i = 0; i < childValues.length; i++)
{
final LValue value = childValues[i];
if (isReferenceChanged(value))
{
return true;
}
}
// LOGGER.debug ("Unchanged.");
return false;
}
public int getState()
{
return state;
}
protected void setState(final int state)
{
this.state = state;
}
}