| /************************************************************** |
| * |
| * 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.OfficeToken; |
| import com.sun.star.report.pentaho.OfficeNamespaces; |
| import com.sun.star.report.pentaho.model.ImageElement; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import org.jfree.layouting.util.AttributeMap; |
| import org.jfree.report.DataSourceException; |
| import org.jfree.report.JFreeReportInfo; |
| import org.jfree.report.ReportDataFactoryException; |
| import org.jfree.report.ReportProcessingException; |
| import org.jfree.report.data.GlobalMasterRow; |
| import org.jfree.report.data.ReportDataRow; |
| import org.jfree.report.expressions.FormulaExpression; |
| import org.jfree.report.flow.FlowController; |
| import org.jfree.report.flow.ReportTarget; |
| import org.jfree.report.flow.layoutprocessor.LayoutController; |
| import org.jfree.report.flow.layoutprocessor.LayoutControllerUtil; |
| import org.jfree.report.structure.Element; |
| import org.jfree.report.structure.Node; |
| import org.jfree.report.structure.Section; |
| import org.jfree.report.util.TextUtilities; |
| |
| import org.pentaho.reporting.libraries.base.util.ObjectUtilities; |
| import org.pentaho.reporting.libraries.formula.Formula; |
| import org.pentaho.reporting.libraries.formula.lvalues.LValue; |
| import org.pentaho.reporting.libraries.formula.parser.ParseException; |
| |
| /** |
| * Produces an image. The image-structures itself (draw:frame and so on) are not generated here. This element produces a |
| * place-holder element and relies on the output target to compute a sensible position for the element. The report |
| * definition does not give any hints about the size of the image, so we have to derive this from the surrounding |
| * context. |
| * |
| * @author Thomas Morgner |
| * @since 05.03.2007 |
| */ |
| public class ImageElementLayoutController |
| extends AbstractReportElementLayoutController |
| { |
| |
| private static final Log LOGGER = LogFactory.getLog(ImageElementLayoutController.class); |
| private ImageElementContext context; |
| |
| public ImageElementLayoutController() |
| { |
| } |
| |
| protected LayoutController delegateContentGeneration(final ReportTarget target) |
| throws ReportProcessingException, ReportDataFactoryException, |
| DataSourceException |
| { |
| final ImageElement imageElement = (ImageElement) getNode(); |
| final FormulaExpression formulaExpression = imageElement.getFormula(); |
| if (formulaExpression == null) |
| { |
| // A static image is easy. At least at this level. Dont ask about the weird things we have to do in the |
| // output targets ... |
| final String linkTarget = imageElement.getImageData(); |
| generateImage(target, linkTarget, imageElement.getScaleMode(), imageElement.isPreserveIRI()); |
| } |
| else |
| { |
| final Object value = |
| LayoutControllerUtil.evaluateExpression(getFlowController(), imageElement, formulaExpression); |
| generateImage(target, value, imageElement.getScaleMode(), imageElement.isPreserveIRI()); |
| } |
| return join(getFlowController()); |
| } |
| |
| private void generateImage(final ReportTarget target, |
| final Object linkTarget, |
| final String scale, |
| final boolean preserveIri) |
| throws ReportProcessingException, DataSourceException |
| { |
| if (linkTarget == null) |
| { |
| return; |
| } |
| |
| final AttributeMap image = new AttributeMap(); |
| image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, Element.NAMESPACE_ATTRIBUTE, JFreeReportInfo.REPORT_NAMESPACE); |
| image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, Element.TYPE_ATTRIBUTE, OfficeToken.IMAGE); |
| image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.SCALE, scale); |
| image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.PRESERVE_IRI, String.valueOf(preserveIri)); |
| image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, "image-context", createContext()); |
| image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.IMAGE_DATA, linkTarget); |
| target.startElement(image); |
| target.endElement(image); |
| } |
| |
| protected ImageElementContext createContext() |
| { |
| if (context == null) |
| { |
| |
| // Step 1: Find the parent cell. |
| final LayoutController cellController = findParentCell(); |
| if (cellController == null) |
| { |
| LOGGER.warn("Image is not contained in a table. Unable to calculate the image-size."); |
| return null; |
| } |
| final Element tableCell = (Element) cellController.getNode(); |
| final int rowSpan = TextUtilities.parseInt((String) tableCell.getAttribute(OfficeNamespaces.TABLE_NS, "number-rows-spanned"), 1); |
| final int colSpan = TextUtilities.parseInt((String) tableCell.getAttribute(OfficeNamespaces.TABLE_NS, "number-columns-spanned"), 1); |
| if (rowSpan < 1 || colSpan < 1) |
| { |
| LOGGER.warn("Rowspan or colspan for image-size calculation was invalid."); |
| return null; |
| } |
| |
| final LayoutController rowController = cellController.getParent(); |
| if (rowController == null) |
| { |
| LOGGER.warn("Table-Cell has no parent. Unable to calculate the image-size."); |
| return null; |
| } |
| final Section tableRow = (Section) rowController.getNode(); |
| // we are now making the assumption, that the row is a section, that contains the table-cell. |
| // This breaks the ability to return nodes or to construct reports on the fly, but the OO-report format |
| // is weird anyway and won't support such advanced techniques for the next few centuries... |
| final int columnPos = findNodeInSection(tableRow, tableCell, OfficeToken.COVERED_TABLE_CELL); |
| if (columnPos == -1) |
| { |
| LOGGER.warn("Table-Cell is not a direct child of the table-row. Unable to calculate the image-size."); |
| return null; |
| } |
| |
| final LayoutController tableController = rowController.getParent(); |
| if (tableController == null) |
| { |
| LOGGER.warn("Table-Row has no Table. Unable to calculate the image-size."); |
| return null; |
| } |
| |
| final Section table = (Section) tableController.getNode(); |
| // ok, we got a table, so as next we have to search for the columns now. |
| final Section columns = (Section) table.findFirstChild(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMNS); |
| if (columns.getNodeCount() <= columnPos + colSpan) |
| { |
| // the colspan is to large. The table definition is therefore invalid. We do not try to fix this. |
| LOGGER.warn( |
| "The Table's defined columns do not match the col-span or col-position. Unable to calculate the image-size."); |
| return null; |
| } |
| |
| final ImageElementContext context = new ImageElementContext(colSpan, rowSpan); |
| addColumnStyles(context, columns, columnPos, colSpan); |
| // finally search the styles for the row now. |
| final int rowPos = findNodeInSection(table, tableRow, null); |
| if (rowPos == -1) |
| { |
| LOGGER.warn("Table-Cell is not a direct child of the table-row. Unable to calculate the image-size."); |
| return null; |
| } |
| |
| addRowStyles(context, table, rowPos, rowSpan); |
| this.context = context; |
| } |
| return this.context; |
| } |
| |
| private int findNodeInSection(final Section tableRow, |
| final Element tableCell, |
| final String secondType) |
| { |
| int retval = 0; |
| final Node[] nodes = tableRow.getNodeArray(); |
| final String namespace = tableCell.getNamespace(); |
| final String type = tableCell.getType(); |
| for (final Node node : nodes) |
| { |
| if (!(node instanceof Element)) |
| { |
| continue; |
| } |
| final Element child = (Element) node; |
| /* |
| if (! OfficeToken.COVERED_TABLE_CELL.equals(child.getType()) && |
| (ObjectUtilities.equal(child.getNamespace(), namespace) == false || |
| ObjectUtilities.equal(child.getType(), type) == false)) |
| */ |
| if (!ObjectUtilities.equal(child.getNamespace(), namespace) || (!ObjectUtilities.equal(child.getType(), type) && (secondType == null || !ObjectUtilities.equal(child.getType(), secondType)))) |
| { |
| continue; |
| } |
| |
| if (node == tableCell) |
| { |
| return retval; |
| } |
| retval += 1; |
| } |
| return -1; |
| } |
| |
| private LayoutController findParentCell() |
| { |
| LayoutController parent = getParent(); |
| while (parent != null) |
| { |
| final Object node = parent.getNode(); |
| if (node instanceof Element) |
| { |
| final Element element = (Element) node; |
| if (OfficeNamespaces.TABLE_NS.equals(element.getNamespace()) && "table-cell".equals(element.getType())) |
| { |
| return parent; |
| } |
| } |
| parent = parent.getParent(); |
| } |
| return null; |
| } |
| |
| protected boolean isValueChanged() |
| { |
| final ImageElement imageElement = (ImageElement) getNode(); |
| final FormulaExpression formulaExpression = imageElement.getFormula(); |
| if (formulaExpression == null) |
| { |
| final FlowController controller = getFlowController(); |
| final GlobalMasterRow masterRow = controller.getMasterRow(); |
| final ReportDataRow reportDataRow = masterRow.getReportDataRow(); |
| return reportDataRow.getCursor() == 0; |
| } |
| |
| try |
| { |
| final Formula formula = formulaExpression.getCompiledFormula(); |
| final LValue lValue = formula.getRootReference(); |
| return isReferenceChanged(lValue); |
| } |
| catch (ParseException e) |
| { |
| return false; |
| } |
| } |
| |
| void addColumnStyles(final ImageElementContext context, final Section columns, final int columnPos, final int colSpan) |
| { |
| final Node[] columnDefs = columns.getNodeArray(); |
| int columnCounter = 0; |
| for (Node columnDef : columnDefs) |
| { |
| final Element column = (Element) columnDef; |
| |
| if (!ObjectUtilities.equal(column.getNamespace(), OfficeNamespaces.TABLE_NS) || !ObjectUtilities.equal(column.getType(), OfficeToken.TABLE_COLUMN)) |
| { |
| continue; |
| } |
| if (columnCounter >= columnPos) |
| { |
| final String colStyle = (String) column.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME); |
| context.setColStyle(columnCounter - columnPos, colStyle); |
| } |
| |
| columnCounter += 1; |
| |
| if (columnCounter >= (columnPos + colSpan)) |
| { |
| break; |
| } |
| |
| } |
| } |
| |
| void addRowStyles(final ImageElementContext context, final Section table, final int rowPos, final int rowSpan) |
| { |
| final Node[] rows = table.getNodeArray(); |
| int rowCounter = 0; |
| for (Node row1 : rows) |
| { |
| final Element row = (Element) row1; |
| |
| if (!ObjectUtilities.equal(row.getNamespace(), OfficeNamespaces.TABLE_NS) || !ObjectUtilities.equal(row.getType(), OfficeToken.TABLE_ROW)) |
| { |
| continue; |
| } |
| if (rowCounter >= rowPos) |
| { |
| final String rowStyle = (String) row.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME); |
| context.setRowStyle(rowCounter - rowPos, rowStyle); |
| } |
| |
| rowCounter += 1; |
| |
| if (rowCounter >= (rowPos + rowSpan)) |
| { |
| break; |
| } |
| } |
| } |
| } |