/*!
* 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) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.designer.core.editor.report.layouting;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import org.pentaho.reporting.designer.core.editor.ReportRenderContext;
import org.pentaho.reporting.designer.core.util.BreakPositionsList;
import org.pentaho.reporting.designer.core.util.exceptions.UncaughtExceptionsModel;
import org.pentaho.reporting.engine.classic.core.Element;
import org.pentaho.reporting.engine.classic.core.IncompatibleFeatureException;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.Section;
import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.SheetLayout;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.TableLayoutProducer;
import org.pentaho.reporting.engine.classic.core.util.InstanceID;
/**
* @author Thomas Morgner
*/
public class SharedElementRenderer
{
private final ReportLayouter reportLayouter;
private final MasterReport masterReport;
private long minimumVersionNeeded;
private EventListenerList listenerList;
private LogicalPageBox pageBox;
private TransferGlobalLayoutProcessStep transferGlobalLayoutProcessor;
private TransferLayoutProcessStep transferLayoutProcessor;
private Map<InstanceID, Set<InstanceID>> conflicts;
private long layoutAge;
private boolean lastResult;
private boolean warnMigration;
private TableLayoutProducer tableLayoutProducer;
private DesignerTableContentProducer tableContentProducer;
public SharedElementRenderer(final ReportRenderContext reportRenderContext)
{
if (reportRenderContext == null)
{
throw new NullPointerException();
}
this.masterReport = reportRenderContext.getMasterReportElement();
if (this.masterReport == null)
{
throw new NullPointerException();
}
this.warnMigration = true;
this.reportLayouter = new ReportLayouter(reportRenderContext);
this.listenerList = new EventListenerList();
this.transferGlobalLayoutProcessor = new TransferGlobalLayoutProcessStep();
this.transferLayoutProcessor = new TransferLayoutProcessStep();
this.conflicts = new HashMap<InstanceID, Set<InstanceID>>();
this.minimumVersionNeeded = -1;
}
public void addChangeListener(final ChangeListener changeListener)
{
listenerList.add(ChangeListener.class, changeListener);
}
public void removeChangeListener(final ChangeListener changeListener)
{
listenerList.remove(ChangeListener.class, changeListener);
}
public void fireChangeEvent()
{
final ChangeEvent ce = new ChangeEvent(this);
final ChangeListener[] changeListeners = listenerList.getListeners(ChangeListener.class);
for (int i = 0; i < changeListeners.length; i++)
{
final ChangeListener listener = changeListeners[i];
listener.stateChanged(ce);
}
}
protected OutputProcessorMetaData getOutputProcessorMetaData()
{
return reportLayouter.getOutputProcessorMetaData();
}
public boolean isLayoutValid()
{
return (this.layoutAge == masterReport.getChangeTracker());
}
public boolean performLayouting()
{
if (this.isLayoutValid())
{
return lastResult;
}
try
{
this.pageBox = reportLayouter.layout();
}
catch (final Exception e)
{
//noinspection ThrowableInstanceNeverThrown
UncaughtExceptionsModel.getInstance().addException(new ReportProcessingException
("Fatal Layouter Error: This report cannot be processed due to a unrecoverable error in the reporting-engine. " +
"Please file a bug-report.", e));
if (warnMigration)
{
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
final IncompatibleFeatureException ife = extractIncompatibleFeatureException(e);
if (ife != null)
{
minimumVersionNeeded = ife.getMinimumVersionNeeded();
}
warnMigration = false;
}
pageBox = null;
}
transferGlobalLayoutProcessor.reset();
try
{
if (pageBox != null)
{
final OutputProcessorMetaData outputProcessorMetaData = getOutputProcessorMetaData();
if (tableLayoutProducer == null)
{
tableLayoutProducer = new TableLayoutProducer(outputProcessorMetaData);
tableLayoutProducer.setProcessWatermark(false);
}
else
{
tableLayoutProducer.clear();
}
// we need to work on a copy here, as the layout computation marks boxes as finished to keep track
// of the progress.
tableLayoutProducer.update(pageBox, false);
if (tableContentProducer == null)
{
tableContentProducer = new DesignerTableContentProducer(tableLayoutProducer.getLayout(), outputProcessorMetaData);
}
else
{
tableContentProducer.reset(tableLayoutProducer.getLayout());
}
conflicts.clear();
conflicts = tableContentProducer.computeConflicts(pageBox, conflicts);
// watermark needs extra pass, or it will produce bogus warnings.
tableLayoutProducer.computeDesigntimeConflicts(pageBox.getWatermarkArea());
final SheetLayout watermarkLayout = tableLayoutProducer.getLayout();
tableContentProducer.reset(watermarkLayout);
conflicts = tableContentProducer.computeWatermarkConflics(pageBox, conflicts);
transferGlobalLayoutProcessor.performTransfer(pageBox, conflicts, masterReport);
}
lastResult = true;
layoutAge = masterReport.getChangeTracker();
fireChangeEvent();
return true;
}
catch (Exception e)
{
UncaughtExceptionsModel.getInstance().addException(e);
lastResult = false;
layoutAge = masterReport.getChangeTracker();
fireChangeEvent();
return false;
}
}
public long getMinimumVersionNeeded()
{
return minimumVersionNeeded;
}
public boolean isMigrationError()
{
return minimumVersionNeeded != -1;
}
public void clearMigrationError()
{
minimumVersionNeeded = -1;
}
private IncompatibleFeatureException extractIncompatibleFeatureException(Throwable e)
{
while (e != null)
{
if (e instanceof IncompatibleFeatureException)
{
return (IncompatibleFeatureException) e;
}
if (e == e.getCause())
{
return null;
}
e = e.getCause();
}
return null;
}
public void transferLocalLayout(final Section section,
final Map<InstanceID, Element> elementsById,
final BreakPositionsList verticalEdgePositions)
{
elementsById.clear();
verticalEdgePositions.clear();
if (pageBox != null)
{
transferLayoutProcessor.performTransfer(section, pageBox, elementsById, verticalEdgePositions);
}
}
public Map<InstanceID, Element> getElementsById()
{
return transferGlobalLayoutProcessor.getElementsById();
}
public Map<InstanceID, Set<InstanceID>> getConflicts()
{
return conflicts;
}
public BreakPositionsList getHorizontalEdgePositions()
{
return transferGlobalLayoutProcessor.getHorizontalEdgePositions();
}
public ReportLayouter getLayouter()
{
return reportLayouter;
}
public LogicalPageBox getPageBox()
{
return pageBox;
}
public Rectangle2D getFallbackBounds()
{
return new Rectangle2D.Float(0, 0, masterReport.getPageDefinition().getWidth(), 0);
}
}