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