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