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: DefaultFlowController.java 3525 2007-10-16 11:43:48Z 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.flow;
032    
033    import org.jfree.report.DataSourceException;
034    import org.jfree.report.ReportDataFactoryException;
035    import org.jfree.report.data.CachingReportDataFactory;
036    import org.jfree.report.data.ExpressionDataRow;
037    import org.jfree.report.data.ExpressionSlot;
038    import org.jfree.report.data.GlobalMasterRow;
039    import org.jfree.report.data.ImportedVariablesDataRow;
040    import org.jfree.report.data.ParameterDataRow;
041    import org.jfree.report.data.PrecomputedValueRegistry;
042    import org.jfree.report.data.PrecomputedValueRegistryBuilder;
043    import org.jfree.report.data.ReportDataRow;
044    import org.jfree.report.util.IntegerCache;
045    import org.jfree.util.FastStack;
046    
047    /**
048     * Creation-Date: 20.02.2006, 15:30:21
049     *
050     * @author Thomas Morgner
051     */
052    public class DefaultFlowController implements FlowController
053    {
054      private static class ReportDataContext
055      {
056        private FastStack markStack;
057        private boolean advanceRequested;
058    
059        private ReportDataContext(final FastStack markStack,
060                                 final boolean advanceRequested)
061        {
062          this.advanceRequested = advanceRequested;
063          this.markStack = markStack;
064        }
065    
066        public boolean isAdvanceRequested()
067        {
068          return advanceRequested;
069        }
070    
071        public FastStack getMarkStack()
072        {
073          return markStack;
074        }
075      }
076    
077      private CachingReportDataFactory reportDataFactory;
078      private GlobalMasterRow dataRow;
079      private boolean advanceRequested;
080      private FastStack reportStack;
081      private FastStack markStack;
082      private FastStack expressionsStack;
083      private String exportDescriptor;
084      private ReportContext reportContext;
085      private ReportJob job;
086      private PrecomputedValueRegistry precomputedValueRegistry;
087    
088      public DefaultFlowController(final ReportContext reportContext,
089                                   final ReportJob job)
090          throws DataSourceException
091      {
092        if (job == null)
093        {
094          throw new NullPointerException();
095        }
096        if (reportContext == null)
097        {
098          throw new NullPointerException();
099        }
100    
101        this.reportContext = reportContext;
102        this.job = job;
103        this.exportDescriptor = reportContext.getExportDescriptor();
104        this.reportDataFactory = new CachingReportDataFactory(job.getDataFactory());
105        this.reportStack = new FastStack();
106        this.markStack = new FastStack();
107        this.expressionsStack = new FastStack();
108        this.advanceRequested = false;
109        this.dataRow = GlobalMasterRow.createReportRow(reportContext);
110        this.dataRow.setParameterDataRow(new ParameterDataRow(job.getParameters()));
111        this.precomputedValueRegistry = new PrecomputedValueRegistryBuilder();
112      }
113    
114      protected DefaultFlowController(final DefaultFlowController fc,
115                                      final GlobalMasterRow dataRow)
116      {
117        this.reportContext = fc.reportContext;
118        this.job = fc.job;
119        this.exportDescriptor = fc.exportDescriptor;
120        this.reportDataFactory = fc.reportDataFactory;
121        this.reportStack = (FastStack) fc.reportStack.clone();
122        this.markStack = (FastStack) fc.markStack.clone();
123        this.expressionsStack = (FastStack) fc.expressionsStack.clone();
124        this.advanceRequested = fc.advanceRequested;
125        this.dataRow = dataRow;
126        this.precomputedValueRegistry = fc.precomputedValueRegistry;
127      }
128    
129    
130      public FlowController performOperation(final FlowControlOperation operation)
131          throws DataSourceException
132      {
133        if (operation == FlowControlOperation.ADVANCE)
134        {
135          if (dataRow.isAdvanceable() && advanceRequested == false)
136          {
137            final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
138            fc.advanceRequested = true;
139            return fc;
140          }
141        }
142        else if (operation == FlowControlOperation.MARK)
143        {
144          final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
145          fc.markStack.push(dataRow);
146          return fc;
147        }
148        else if (operation == FlowControlOperation.RECALL)
149        {
150          if (markStack.isEmpty())
151          {
152            return this;
153          }
154    
155          final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
156          fc.dataRow = (GlobalMasterRow) fc.markStack.pop();
157          fc.advanceRequested = false;
158          return fc;
159        }
160        else if (operation == FlowControlOperation.DONE)
161        {
162          // do not change the current data row..
163    
164          final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
165          fc.markStack.pop();
166          return fc;
167        }
168        else if (operation == FlowControlOperation.COMMIT)
169        {
170          if (isAdvanceRequested())
171          {
172            final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
173            fc.dataRow = dataRow.advance();
174            fc.advanceRequested = false;
175            return fc;
176          }
177        }
178        return this;
179      }
180    
181      public GlobalMasterRow getMasterRow()
182      {
183        return dataRow;
184      }
185    
186    
187      public boolean isAdvanceRequested()
188      {
189        return advanceRequested;
190      }
191    
192      /**
193       * This should be called only once per report processing. A JFreeReport object
194       * defines the global master report - all other reports are subreport
195       * instances.
196       * <p/>
197       * The global master report receives its parameter set from the
198       * Job-Definition, while subreports will read their parameters from the
199       * current datarow state.
200       *
201       * @param query
202       * @return
203       * @throws ReportDataFactoryException
204       * @throws DataSourceException
205       */
206      public FlowController performQuery(final String query)
207          throws ReportDataFactoryException, DataSourceException
208      {
209    
210        final GlobalMasterRow masterRow =
211            GlobalMasterRow.createReportRow(dataRow, reportContext);
212        masterRow.setParameterDataRow(new ParameterDataRow
213            (getReportJob().getParameters()));
214    
215        masterRow.setReportDataRow(ReportDataRow.createDataRow
216            (reportDataFactory, query, dataRow.getGlobalView()));
217    
218        final DefaultFlowController fc = new DefaultFlowController(this, masterRow);
219        fc.reportStack.push(new ReportDataContext(fc.markStack, advanceRequested));
220        fc.markStack = new FastStack();
221        fc.dataRow = masterRow;
222        return fc;
223      }
224    
225      public FlowController performSubReportQuery(final String query,
226                                                  final ParameterMapping[] inputParameters,
227                                                  final ParameterMapping[] outputParameters
228                                                  )
229          throws ReportDataFactoryException, DataSourceException
230      {
231        final GlobalMasterRow outerRow = dataRow.derive();
232    
233        // create a view for the parameters of the report ...
234        final GlobalMasterRow masterRow =
235            GlobalMasterRow.createReportRow(outerRow, reportContext);
236    
237        masterRow.setParameterDataRow
238            (new ParameterDataRow(inputParameters, outerRow.getGlobalView()));
239    
240        // perform the query ...
241        // add the resultset ...
242        masterRow.setReportDataRow(ReportDataRow.createDataRow
243            (reportDataFactory, query, masterRow.getGlobalView()));
244    
245        if (outputParameters == null)
246        {
247          outerRow.setExportedDataRow(new ImportedVariablesDataRow(masterRow));
248        }
249        else
250        {
251          // check and rebuild the parameter mapping from the inner to the outer
252          // context. Only deep-traversal expressions will be able to see these
253          // values (unless they have been defined as local variables).
254          outerRow.setExportedDataRow(new ImportedVariablesDataRow
255              (masterRow, outputParameters));
256        }
257    
258        final DefaultFlowController fc = new DefaultFlowController(this, masterRow);
259        fc.reportStack.push(new ReportDataContext(fc.markStack, advanceRequested));
260        fc.markStack = new FastStack();
261        fc.dataRow = masterRow;
262        return fc;
263      }
264    
265      public FlowController activateExpressions(final ExpressionSlot[] expressions)
266          throws DataSourceException
267      {
268        if (expressions.length == 0)
269        {
270          final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
271          fc.expressionsStack.push(IntegerCache.getInteger(0));
272          return fc;
273        }
274    
275        final GlobalMasterRow dataRow = this.dataRow.derive();
276        final ExpressionDataRow edr = dataRow.getExpressionDataRow();
277        edr.pushExpressions(expressions);
278    
279        final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
280        final Integer exCount = IntegerCache.getInteger(expressions.length);
281        fc.expressionsStack.push(exCount);
282        return fc;
283      }
284    
285      public FlowController deactivateExpressions() throws DataSourceException
286      {
287        final Integer counter = (Integer) this.expressionsStack.peek();
288        final int counterRaw = counter.intValue();
289        if (counterRaw == 0)
290        {
291          final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
292          fc.expressionsStack.pop();
293          return fc;
294        }
295    
296        final GlobalMasterRow dataRow = this.dataRow.derive();
297        final ExpressionDataRow edr = dataRow.getExpressionDataRow();
298    
299        final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
300        fc.expressionsStack.pop();
301        edr.popExpressions(counterRaw);
302        return fc;
303      }
304    
305      public FlowController performReturnFromQuery() throws DataSourceException
306      {
307        final DefaultFlowController fc = new DefaultFlowController(this, dataRow);
308        final ReportDataRow reportDataRow = dataRow.getReportDataRow();
309        if (reportDataRow == null)
310        {
311          return this;
312        }
313        // We dont close the report data, as some previously saved states may
314        // still reference it. (The caching report data factory takes care of
315        // that later.)
316    
317        final ReportDataContext context = (ReportDataContext) fc.reportStack.pop();
318        fc.dataRow = dataRow.getParentDataRow();
319        fc.dataRow = fc.dataRow.derive();
320        fc.dataRow.setExportedDataRow(null);
321        fc.markStack = context.getMarkStack();
322        fc.advanceRequested = context.isAdvanceRequested();
323        return fc;
324      }
325    
326      public ReportJob getReportJob()
327      {
328        return job;
329      }
330    
331      public String getExportDescriptor()
332      {
333        return exportDescriptor;
334      }
335    
336      public ReportContext getReportContext()
337      {
338        return reportContext;
339      }
340    
341      /**
342       * Returns the current expression slots of all currently active expressions.
343       *
344       * @return
345       * @throws org.jfree.report.DataSourceException
346       *
347       */
348      public ExpressionSlot[] getActiveExpressions() throws DataSourceException
349      {
350        return dataRow.getExpressionDataRow().getSlots();
351      }
352    
353      public FlowController createPrecomputeInstance() throws DataSourceException
354      {
355        final DefaultFlowController precompute = new DefaultFlowController(this, dataRow.derive());
356        precompute.precomputedValueRegistry = new PrecomputedValueRegistryBuilder();
357        return precompute;
358      }
359    
360      public PrecomputedValueRegistry getPrecomputedValueRegistry()
361      {
362        return precomputedValueRegistry;
363      }
364    }