1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 package com.sun.star.report.pentaho.layoutprocessor; 24 25 import com.sun.star.report.OfficeToken; 26 import com.sun.star.report.pentaho.OfficeNamespaces; 27 import com.sun.star.report.pentaho.model.ImageElement; 28 29 import org.apache.commons.logging.Log; 30 import org.apache.commons.logging.LogFactory; 31 32 import org.jfree.layouting.util.AttributeMap; 33 import org.jfree.report.DataSourceException; 34 import org.jfree.report.JFreeReportInfo; 35 import org.jfree.report.ReportDataFactoryException; 36 import org.jfree.report.ReportProcessingException; 37 import org.jfree.report.data.GlobalMasterRow; 38 import org.jfree.report.data.ReportDataRow; 39 import org.jfree.report.expressions.FormulaExpression; 40 import org.jfree.report.flow.FlowController; 41 import org.jfree.report.flow.ReportTarget; 42 import org.jfree.report.flow.layoutprocessor.LayoutController; 43 import org.jfree.report.flow.layoutprocessor.LayoutControllerUtil; 44 import org.jfree.report.structure.Element; 45 import org.jfree.report.structure.Node; 46 import org.jfree.report.structure.Section; 47 import org.jfree.report.util.TextUtilities; 48 49 import org.pentaho.reporting.libraries.base.util.ObjectUtilities; 50 import org.pentaho.reporting.libraries.formula.Formula; 51 import org.pentaho.reporting.libraries.formula.lvalues.LValue; 52 import org.pentaho.reporting.libraries.formula.parser.ParseException; 53 54 /** 55 * Produces an image. The image-structures itself (draw:frame and so on) are not generated here. This element produces a 56 * place-holder element and relies on the output target to compute a sensible position for the element. The report 57 * definition does not give any hints about the size of the image, so we have to derive this from the surrounding 58 * context. 59 * 60 * @author Thomas Morgner 61 * @since 05.03.2007 62 */ 63 public class ImageElementLayoutController 64 extends AbstractReportElementLayoutController 65 { 66 67 private static final Log LOGGER = LogFactory.getLog(ImageElementLayoutController.class); 68 private ImageElementContext context; 69 ImageElementLayoutController()70 public ImageElementLayoutController() 71 { 72 } 73 delegateContentGeneration(final ReportTarget target)74 protected LayoutController delegateContentGeneration(final ReportTarget target) 75 throws ReportProcessingException, ReportDataFactoryException, 76 DataSourceException 77 { 78 final ImageElement imageElement = (ImageElement) getNode(); 79 final FormulaExpression formulaExpression = imageElement.getFormula(); 80 if (formulaExpression == null) 81 { 82 // A static image is easy. At least at this level. Dont ask about the weird things we have to do in the 83 // output targets ... 84 final String linkTarget = imageElement.getImageData(); 85 generateImage(target, linkTarget, imageElement.getScaleMode(), imageElement.isPreserveIRI()); 86 } 87 else 88 { 89 final Object value = 90 LayoutControllerUtil.evaluateExpression(getFlowController(), imageElement, formulaExpression); 91 generateImage(target, value, imageElement.getScaleMode(), imageElement.isPreserveIRI()); 92 } 93 return join(getFlowController()); 94 } 95 generateImage(final ReportTarget target, final Object linkTarget, final String scale, final boolean preserveIri)96 private void generateImage(final ReportTarget target, 97 final Object linkTarget, 98 final String scale, 99 final boolean preserveIri) 100 throws ReportProcessingException, DataSourceException 101 { 102 if (linkTarget == null) 103 { 104 return; 105 } 106 107 final AttributeMap image = new AttributeMap(); 108 image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, Element.NAMESPACE_ATTRIBUTE, JFreeReportInfo.REPORT_NAMESPACE); 109 image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, Element.TYPE_ATTRIBUTE, OfficeToken.IMAGE); 110 image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.SCALE, scale); 111 image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.PRESERVE_IRI, String.valueOf(preserveIri)); 112 image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, "image-context", createContext()); 113 image.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.IMAGE_DATA, linkTarget); 114 target.startElement(image); 115 target.endElement(image); 116 } 117 createContext()118 protected ImageElementContext createContext() 119 { 120 if (context == null) 121 { 122 123 // Step 1: Find the parent cell. 124 final LayoutController cellController = findParentCell(); 125 if (cellController == null) 126 { 127 LOGGER.warn("Image is not contained in a table. Unable to calculate the image-size."); 128 return null; 129 } 130 final Element tableCell = (Element) cellController.getNode(); 131 final int rowSpan = TextUtilities.parseInt((String) tableCell.getAttribute(OfficeNamespaces.TABLE_NS, "number-rows-spanned"), 1); 132 final int colSpan = TextUtilities.parseInt((String) tableCell.getAttribute(OfficeNamespaces.TABLE_NS, "number-columns-spanned"), 1); 133 if (rowSpan < 1 || colSpan < 1) 134 { 135 LOGGER.warn("Rowspan or colspan for image-size calculation was invalid."); 136 return null; 137 } 138 139 final LayoutController rowController = cellController.getParent(); 140 if (rowController == null) 141 { 142 LOGGER.warn("Table-Cell has no parent. Unable to calculate the image-size."); 143 return null; 144 } 145 final Section tableRow = (Section) rowController.getNode(); 146 // we are now making the assumption, that the row is a section, that contains the table-cell. 147 // This breaks the ability to return nodes or to construct reports on the fly, but the OO-report format 148 // is weird anyway and won't support such advanced techniques for the next few centuries... 149 final int columnPos = findNodeInSection(tableRow, tableCell, OfficeToken.COVERED_TABLE_CELL); 150 if (columnPos == -1) 151 { 152 LOGGER.warn("Table-Cell is not a direct child of the table-row. Unable to calculate the image-size."); 153 return null; 154 } 155 156 final LayoutController tableController = rowController.getParent(); 157 if (tableController == null) 158 { 159 LOGGER.warn("Table-Row has no Table. Unable to calculate the image-size."); 160 return null; 161 } 162 163 final Section table = (Section) tableController.getNode(); 164 // ok, we got a table, so as next we have to search for the columns now. 165 final Section columns = (Section) table.findFirstChild(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMNS); 166 if (columns.getNodeCount() <= columnPos + colSpan) 167 { 168 // the colspan is to large. The table definition is therefore invalid. We do not try to fix this. 169 LOGGER.warn( 170 "The Table's defined columns do not match the col-span or col-position. Unable to calculate the image-size."); 171 return null; 172 } 173 174 final ImageElementContext context = new ImageElementContext(colSpan, rowSpan); 175 addColumnStyles(context, columns, columnPos, colSpan); 176 // finally search the styles for the row now. 177 final int rowPos = findNodeInSection(table, tableRow, null); 178 if (rowPos == -1) 179 { 180 LOGGER.warn("Table-Cell is not a direct child of the table-row. Unable to calculate the image-size."); 181 return null; 182 } 183 184 addRowStyles(context, table, rowPos, rowSpan); 185 this.context = context; 186 } 187 return this.context; 188 } 189 findNodeInSection(final Section tableRow, final Element tableCell, final String secondType)190 private int findNodeInSection(final Section tableRow, 191 final Element tableCell, 192 final String secondType) 193 { 194 int retval = 0; 195 final Node[] nodes = tableRow.getNodeArray(); 196 final String namespace = tableCell.getNamespace(); 197 final String type = tableCell.getType(); 198 for (final Node node : nodes) 199 { 200 if (!(node instanceof Element)) 201 { 202 continue; 203 } 204 final Element child = (Element) node; 205 /* 206 if (! OfficeToken.COVERED_TABLE_CELL.equals(child.getType()) && 207 (ObjectUtilities.equal(child.getNamespace(), namespace) == false || 208 ObjectUtilities.equal(child.getType(), type) == false)) 209 */ 210 if (!ObjectUtilities.equal(child.getNamespace(), namespace) || (!ObjectUtilities.equal(child.getType(), type) && (secondType == null || !ObjectUtilities.equal(child.getType(), secondType)))) 211 { 212 continue; 213 } 214 215 if (node == tableCell) 216 { 217 return retval; 218 } 219 retval += 1; 220 } 221 return -1; 222 } 223 findParentCell()224 private LayoutController findParentCell() 225 { 226 LayoutController parent = getParent(); 227 while (parent != null) 228 { 229 final Object node = parent.getNode(); 230 if (node instanceof Element) 231 { 232 final Element element = (Element) node; 233 if (OfficeNamespaces.TABLE_NS.equals(element.getNamespace()) && "table-cell".equals(element.getType())) 234 { 235 return parent; 236 } 237 } 238 parent = parent.getParent(); 239 } 240 return null; 241 } 242 isValueChanged()243 protected boolean isValueChanged() 244 { 245 final ImageElement imageElement = (ImageElement) getNode(); 246 final FormulaExpression formulaExpression = imageElement.getFormula(); 247 if (formulaExpression == null) 248 { 249 final FlowController controller = getFlowController(); 250 final GlobalMasterRow masterRow = controller.getMasterRow(); 251 final ReportDataRow reportDataRow = masterRow.getReportDataRow(); 252 return reportDataRow.getCursor() == 0; 253 } 254 255 try 256 { 257 final Formula formula = formulaExpression.getCompiledFormula(); 258 final LValue lValue = formula.getRootReference(); 259 return isReferenceChanged(lValue); 260 } 261 catch (ParseException e) 262 { 263 return false; 264 } 265 } 266 addColumnStyles(final ImageElementContext context, final Section columns, final int columnPos, final int colSpan)267 void addColumnStyles(final ImageElementContext context, final Section columns, final int columnPos, final int colSpan) 268 { 269 final Node[] columnDefs = columns.getNodeArray(); 270 int columnCounter = 0; 271 for (Node columnDef : columnDefs) 272 { 273 final Element column = (Element) columnDef; 274 275 if (!ObjectUtilities.equal(column.getNamespace(), OfficeNamespaces.TABLE_NS) || !ObjectUtilities.equal(column.getType(), OfficeToken.TABLE_COLUMN)) 276 { 277 continue; 278 } 279 if (columnCounter >= columnPos) 280 { 281 final String colStyle = (String) column.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME); 282 context.setColStyle(columnCounter - columnPos, colStyle); 283 } 284 285 columnCounter += 1; 286 287 if (columnCounter >= (columnPos + colSpan)) 288 { 289 break; 290 } 291 292 } 293 } 294 addRowStyles(final ImageElementContext context, final Section table, final int rowPos, final int rowSpan)295 void addRowStyles(final ImageElementContext context, final Section table, final int rowPos, final int rowSpan) 296 { 297 final Node[] rows = table.getNodeArray(); 298 int rowCounter = 0; 299 for (Node row1 : rows) 300 { 301 final Element row = (Element) row1; 302 303 if (!ObjectUtilities.equal(row.getNamespace(), OfficeNamespaces.TABLE_NS) || !ObjectUtilities.equal(row.getType(), OfficeToken.TABLE_ROW)) 304 { 305 continue; 306 } 307 if (rowCounter >= rowPos) 308 { 309 final String rowStyle = (String) row.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME); 310 context.setRowStyle(rowCounter - rowPos, rowStyle); 311 } 312 313 rowCounter += 1; 314 315 if (rowCounter >= (rowPos + rowSpan)) 316 { 317 break; 318 } 319 } 320 } 321 } 322