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.pentaho.model.OfficeGroupSection;
26 import com.sun.star.report.pentaho.model.ReportElement;
27 
28 import org.jfree.report.DataFlags;
29 import org.jfree.report.DataRow;
30 import org.jfree.report.DataSourceException;
31 import org.jfree.report.ReportDataFactoryException;
32 import org.jfree.report.ReportProcessingException;
33 import org.jfree.report.expressions.Expression;
34 import org.jfree.report.flow.FlowController;
35 import org.jfree.report.flow.ReportTarget;
36 import org.jfree.report.flow.layoutprocessor.AbstractLayoutController;
37 import org.jfree.report.flow.layoutprocessor.LayoutController;
38 import org.jfree.report.flow.layoutprocessor.LayoutControllerUtil;
39 import org.jfree.report.flow.layoutprocessor.SectionLayoutController;
40 import org.jfree.report.structure.Element;
41 import org.jfree.report.structure.Group;
42 
43 import org.pentaho.reporting.libraries.formula.lvalues.ContextLookup;
44 import org.pentaho.reporting.libraries.formula.lvalues.LValue;
45 
46 /**
47  * Todo: Document me!
48  *
49  * @author Thomas Morgner
50  * @since 05.03.2007
51  * @noinspection CloneableClassWithoutClone
52  */
53 public abstract class AbstractReportElementLayoutController
54         extends AbstractLayoutController
55 {
56 
57     public static final int NOT_STARTED = 0;
58     public static final int FINISHED = 2;
59     private int state;
60 
AbstractReportElementLayoutController()61     protected AbstractReportElementLayoutController()
62     {
63     }
64 
65     /**
66      * Advances the processing position.
67      *
68      * @param target the report target that receives generated events.
69      * @return the new layout controller instance representing the new state.
70      *
71      * @throws org.jfree.report.DataSourceException        if there was a problem reading data from
72      *                                    the datasource.
73      * @throws org.jfree.report.ReportProcessingException  if there was a general problem during
74      *                                    the report processing.
75      * @throws org.jfree.report.ReportDataFactoryException if a query failed.
76      */
advance(final ReportTarget target)77     public LayoutController advance(final ReportTarget target)
78             throws DataSourceException, ReportDataFactoryException,
79             ReportProcessingException
80     {
81         if (state != AbstractReportElementLayoutController.NOT_STARTED)
82         {
83             throw new IllegalStateException();
84         }
85 
86         boolean isPrintableContent = true;
87         final ReportElement text = (ReportElement) getNode();
88         // Tests we have to perform:
89         // 1. Print when group changes. We can know whether a group changed by
90         //    looking at the newly introduced iteration counter.
91         //
92         //    Whether we use the next one or the one after that depends on whether
93         //    this element is a child of a group-header or group-footer.
94 
95         // 2. Print repeated values. This never applies to static text or static
96         //    elements.
97         if ((text.isPrintWhenGroupChanges() && !isGroupChanged()) || (!text.isPrintRepeatedValues() && !isValueChanged()))
98         {
99             // if this is set to true, then we print the element only if this is the
100             // first occurrence in this group.
101             // or
102             // If this is set to true, we evaluate the formula of the element and
103             // try to derive whether there was a change.
104             isPrintableContent = false;
105         }
106 
107         // 3. Evaluate the Display Condition
108         final Expression dc = text.getDisplayCondition();
109         if (dc != null)
110         {
111             final Object o = LayoutControllerUtil.evaluateExpression(getFlowController(), text, dc);
112             if (Boolean.FALSE.equals(o))
113             {
114 //        LOGGER.debug ("DISPLAY Condition forbids printing");
115                 isPrintableContent = false;
116             }
117         }
118 
119         if (!isPrintableContent)
120         {
121             // There is no printable content at all. Set the state to FINISHED
122             return join(getFlowController());
123         }
124         else
125         {
126             // delegate to the handler ..
127             return delegateContentGeneration(target);
128         }
129 
130     }
131 
isValueChanged()132     protected abstract boolean isValueChanged();
133 
isGroupChanged()134     protected boolean isGroupChanged()
135     {
136         // search the group.
137         final SectionLayoutController slc = findGroup();
138         if (slc == null)
139         {
140             // Always print the content of the report header and footer and
141             // the page header and footer.
142             return true;
143         }
144 
145         // we are in the first iteration, so yes, the group has changed recently.
146         return slc.getIterationCount() == 0;
147     }
148 
findGroup()149     private SectionLayoutController findGroup()
150     {
151         LayoutController parent = getParent();
152         boolean skipNext = false;
153         while (parent != null)
154         {
155             if (!(parent instanceof SectionLayoutController))
156             {
157                 parent = parent.getParent();
158             }
159             else
160             {
161                 final SectionLayoutController slc = (SectionLayoutController) parent;
162                 final Element element = slc.getElement();
163                 if (element instanceof OfficeGroupSection)
164                 {
165                     // This is a header or footer. So we take the next group instead.
166                     skipNext = true;
167                     parent = parent.getParent();
168                 }
169                 else if (!(element instanceof Group))
170                 {
171                     parent = parent.getParent();
172                 }
173                 else if (skipNext)
174                 {
175                     skipNext = false;
176                     parent = parent.getParent();
177                 }
178                 else
179                 {
180                     return (SectionLayoutController) parent;
181                 }
182             }
183         }
184         return null;
185     }
186 
187     /**
188      * Joins with a delegated process flow. This is generally called from a child
189      * flow and should *not* (I mean it!) be called from outside. If you do,
190      * you'll suffer.
191      *
192      * @param flowController the flow controller of the parent.
193      * @return the joined layout controller that incorperates all changes from the
194      *         delegate.
195      */
join(final FlowController flowController)196     public LayoutController join(final FlowController flowController)
197             throws DataSourceException, ReportDataFactoryException,
198             ReportProcessingException
199     {
200         final AbstractReportElementLayoutController alc =
201                 (AbstractReportElementLayoutController) clone();
202         alc.state = AbstractReportElementLayoutController.FINISHED;
203         return alc;
204     }
205 
delegateContentGeneration(final ReportTarget target)206     protected abstract LayoutController delegateContentGeneration(final ReportTarget target)
207             throws ReportProcessingException, ReportDataFactoryException,
208             DataSourceException;
209 
210     /**
211      * Checks, whether the layout controller would be advanceable. If this method
212      * returns true, it is generally safe to call the 'advance()' method.
213      *
214      * @return true, if the layout controller is advanceable, false otherwise.
215      */
isAdvanceable()216     public boolean isAdvanceable()
217     {
218         return state != AbstractReportElementLayoutController.FINISHED;
219     }
220 
isReferenceChanged(final LValue lValue)221     protected boolean isReferenceChanged(final LValue lValue)
222     {
223         if (lValue instanceof ContextLookup)
224         {
225             final ContextLookup rval = (ContextLookup) lValue;
226             final String s = rval.getName();
227             final DataRow view = getFlowController().getMasterRow().getGlobalView();
228             try
229             {
230                 final DataFlags flags = view.getFlags(s);
231                 if (flags != null && flags.isChanged())
232                 {
233 //            LOGGER.debug ("Reference " + s + " is changed");
234                     return true;
235                 }
236 //        LOGGER.debug ("Reference " + s + " is unchanged");
237             }
238             catch (DataSourceException e)
239             {
240                 // ignore .. assume that the reference has not changed.
241             }
242         }
243         final LValue[] childValues = lValue.getChildValues();
244         for (int i = 0; i < childValues.length; i++)
245         {
246             final LValue value = childValues[i];
247             if (isReferenceChanged(value))
248             {
249                 return true;
250             }
251         }
252 //    LOGGER.debug ("Unchanged.");
253         return false;
254     }
255 
getState()256     public int getState()
257     {
258         return state;
259     }
260 
setState(final int state)261     protected void setState(final int state)
262     {
263         this.state = state;
264     }
265 }
266