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    }