001 /** 002 * ======================================== 003 * JFreeReport : a free Java report library 004 * ======================================== 005 * 006 * Project Info: http://reporting.pentaho.org/ 007 * 008 * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors. 009 * 010 * This library is free software; you can redistribute it and/or modify it under the terms 011 * of the GNU Lesser General Public License as published by the Free Software Foundation; 012 * either version 2.1 of the License, or (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 015 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 016 * See the GNU Lesser General Public License for more details. 017 * 018 * You should have received a copy of the GNU Lesser General Public License along with this 019 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 020 * Boston, MA 02111-1307, USA. 021 * 022 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 023 * in the United States and other countries.] 024 * 025 * ------------ 026 * $Id: FormulaFunction.java 3048 2007-07-28 18:02:42Z tmorgner $ 027 * ------------ 028 * (C) Copyright 2000-2005, by Object Refinery Limited. 029 * (C) Copyright 2005-2007, by Pentaho Corporation. 030 */ 031 package org.jfree.report.expressions; 032 033 import org.jfree.formula.Formula; 034 import org.jfree.formula.FormulaContext; 035 import org.jfree.formula.parser.ParseException; 036 import org.jfree.report.DataSourceException; 037 import org.jfree.report.flow.ReportContext; 038 import org.jfree.util.Log; 039 040 /** 041 * Creation-Date: 04.11.2006, 19:24:04 042 * 043 * @author Thomas Morgner 044 */ 045 public class FormulaFunction extends AbstractExpression implements Function 046 { 047 private String formulaNamespace; 048 private String formulaExpression; 049 private String formula; 050 051 private String initialNamespace; 052 private String initialExpression; 053 private String initial; 054 private transient Formula compiledFormula; 055 private boolean initialized; 056 057 public FormulaFunction() 058 { 059 } 060 061 private synchronized FormulaContext getFormulaContext() 062 { 063 final ReportContext globalContext = getRuntime().getReportContext(); 064 return globalContext.getFormulaContext(); 065 } 066 067 public String getInitial() 068 { 069 return initial; 070 } 071 072 public String getInitialExpression() 073 { 074 return initialExpression; 075 } 076 077 public String getInitialNamespace() 078 { 079 return initialNamespace; 080 } 081 082 public void setInitial(final String initial) 083 { 084 this.initial = initial; 085 if (initial == null) 086 { 087 initialNamespace = null; 088 initialExpression = null; 089 } 090 else 091 { 092 final int separator = initial.indexOf(':'); 093 if (separator <= 0 || ((separator + 1) == initial.length())) 094 { 095 if (formula.startsWith("=")) 096 { 097 initialNamespace = "report"; 098 initialExpression = initial.substring(1); 099 } 100 else 101 { 102 // error: invalid formula. 103 initialNamespace = null; 104 initialExpression = null; 105 } 106 } 107 else 108 { 109 initialNamespace = initial.substring(0, separator); 110 initialExpression = initial.substring(separator + 1); 111 } 112 } 113 } 114 115 116 public String getFormula() 117 { 118 return formula; 119 } 120 121 public String getFormulaNamespace() 122 { 123 return formulaNamespace; 124 } 125 126 public String getFormulaExpression() 127 { 128 return formulaExpression; 129 } 130 131 public void setFormula(final String formula) 132 { 133 this.formula = formula; 134 if (formula == null) 135 { 136 formulaNamespace = null; 137 formulaExpression = null; 138 } 139 else 140 { 141 final int separator = formula.indexOf(':'); 142 if (separator <= 0 || ((separator + 1) == formula.length())) 143 { 144 if (formula.startsWith("=")) 145 { 146 formulaNamespace = "report"; 147 formulaExpression = formula.substring(1); 148 } 149 else 150 { 151 // error: invalid formula. 152 formulaNamespace = null; 153 formulaExpression = null; 154 } 155 } 156 else 157 { 158 formulaNamespace = formula.substring(0, separator); 159 formulaExpression = formula.substring(separator + 1); 160 } 161 } 162 this.compiledFormula = null; 163 } 164 165 /** 166 * When the advance method is called, the function is asked to perform the 167 * next step of its computation. 168 * <p/> 169 * The original function must not be altered during that step (or more 170 * correctly, calling advance on the original expression again must not return 171 * a different result). 172 * 173 * @return a copy of the function containing the new state. 174 */ 175 public Function advance() throws DataSourceException 176 { 177 try 178 { 179 return (Function) clone(); 180 } 181 catch (CloneNotSupportedException e) 182 { 183 throw new DataSourceException("Unable to derive a new instance"); 184 } 185 } 186 187 private Object computeInitialValue() 188 { 189 try 190 { 191 if (initial != null) 192 { 193 final Formula initFormula = new Formula(initialExpression); 194 final ReportFormulaContext context = 195 new ReportFormulaContext(getFormulaContext(), getDataRow()); 196 context.setDeclaringElement(getRuntime().getDeclaringParent()); 197 try 198 { 199 initFormula.initialize(context); 200 return initFormula.evaluate(); 201 } 202 finally 203 { 204 context.setDeclaringElement(null); 205 context.setDataRow(null); 206 } 207 } 208 209 // if the code above did not trigger, compute a regular thing .. 210 return computeRegularValue(); 211 } 212 catch (Exception e) 213 { 214 Log.debug ("Failed to compute the initial value."); 215 return null; 216 } 217 } 218 219 private Object computeRegularValue() 220 { 221 try 222 { 223 if (compiledFormula == null) 224 { 225 compiledFormula = new Formula(formulaExpression); 226 } 227 228 final ReportFormulaContext context = 229 new ReportFormulaContext(getFormulaContext(), getDataRow()); 230 context.setDeclaringElement(getRuntime().getDeclaringParent()); 231 try 232 { 233 compiledFormula.initialize(context); 234 return compiledFormula.evaluate(); 235 } 236 finally 237 { 238 context.setDeclaringElement(null); 239 context.setDataRow(null); 240 } 241 } 242 catch (Exception e) 243 { 244 Log.debug ("Failed to compute the regular value.", e); 245 return null; 246 } 247 } 248 249 /** 250 * Return the current expression value. <P> The value depends (obviously) on 251 * the expression implementation. 252 * 253 * @return the value of the function. 254 */ 255 public Object computeValue() throws DataSourceException 256 { 257 try 258 { 259 if (initialized == false) 260 { 261 initialized = true; 262 return computeInitialValue(); 263 } 264 return computeRegularValue(); 265 } 266 catch (Exception e) 267 { 268 return null; 269 } 270 } 271 272 /** 273 * Clones the expression, expression should be reinitialized after the 274 * cloning. <P> Expression maintain no state, cloning is done at the beginning 275 * of the report processing to disconnect the used expression from any other 276 * object space. 277 * 278 * @return A clone of this expression. 279 * @throws CloneNotSupportedException this should never happen. 280 */ 281 public Object clone() throws CloneNotSupportedException 282 { 283 final FormulaFunction o = (FormulaFunction) super.clone(); 284 if (compiledFormula != null) 285 { 286 o.compiledFormula = (Formula) compiledFormula.clone(); 287 } 288 return o; 289 } 290 291 292 /** 293 * Returns the compiled formula. The formula is not connected to a formula 294 * context. 295 * 296 * @return the formula. 297 * @throws org.jfree.formula.parser.ParseException if the formula contains syntax errors. 298 */ 299 public Formula getCompiledFormula() 300 throws ParseException 301 { 302 if (compiledFormula == null) 303 { 304 compiledFormula = new Formula(formulaExpression); 305 } 306 return compiledFormula; 307 } 308 }