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: AutoTableLayoutController.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    
032    package org.jfree.report.modules.misc.autotable.flow;
033    
034    import org.jfree.layouting.util.AttributeMap;
035    import org.jfree.report.DataSourceException;
036    import org.jfree.report.ReportDataFactoryException;
037    import org.jfree.report.ReportProcessingException;
038    import org.jfree.report.data.ReportDataRow;
039    import org.jfree.report.flow.FlowControlOperation;
040    import org.jfree.report.flow.FlowController;
041    import org.jfree.report.flow.ReportContext;
042    import org.jfree.report.flow.ReportTarget;
043    import org.jfree.report.flow.layoutprocessor.ElementLayoutController;
044    import org.jfree.report.flow.layoutprocessor.LayoutController;
045    import org.jfree.report.flow.layoutprocessor.LayoutControllerFactory;
046    import org.jfree.report.flow.layoutprocessor.LayoutControllerUtil;
047    import org.jfree.report.modules.misc.autotable.AutoTableElement;
048    import org.jfree.report.modules.misc.autotable.AutoTableModule;
049    
050    /**
051     * Creation-Date: Dec 9, 2006, 6:05:58 PM
052     *
053     * @author Thomas Morgner
054     */
055    public class AutoTableLayoutController extends ElementLayoutController
056    {
057      public static final int HANDLING_HEADER = 0;
058      public static final int HANDLING_DATA = 1;
059      public static final int HANDLING_FOOTER = 2;
060    
061      private int currentColumn;
062      private int processingState;
063      private int columnCount;
064    
065      public AutoTableLayoutController()
066      {
067      }
068    
069      public void initialize(final Object node, final FlowController flowController, final LayoutController parent)
070          throws DataSourceException, ReportDataFactoryException, ReportProcessingException
071      {
072        super.initialize(node, flowController, parent);
073        final ReportDataRow reportDataRow =
074            flowController.getMasterRow().getReportDataRow();
075        this.columnCount = reportDataRow.getColumnCount();
076      }
077    
078      protected LayoutController processContent(final ReportTarget target)
079          throws DataSourceException, ReportProcessingException, ReportDataFactoryException
080      {
081        switch (processingState)
082        {
083          case AutoTableLayoutController.HANDLING_HEADER:
084            return processHeader(target);
085          case AutoTableLayoutController.HANDLING_FOOTER:
086            return processFooter(target);
087          case AutoTableLayoutController.HANDLING_DATA:
088            return processData(target);
089          default:
090            throw new ReportProcessingException("No such state.");
091        }
092    
093      }
094    
095      private LayoutController processData(final ReportTarget target)
096          throws ReportProcessingException, DataSourceException, ReportDataFactoryException
097      {
098        // the auto-table is responsible for the iteration over the table.
099        final AutoTableElement node = (AutoTableElement) getElement();
100        if (node.getContentCount() == 0)
101        {
102          throw new ReportProcessingException
103              ("An Auto-Table must have at least one defined column.");
104        }
105    
106        if (currentColumn == 0)
107        {
108          // Start a new table-header section ..
109          final AttributeMap elementMap = LayoutControllerUtil.createEmptyMap
110              (AutoTableModule.AUTOTABLE_NAMESPACE, "data-row");
111          target.startElement(elementMap);
112        }
113    
114        if (currentColumn < columnCount)
115        {
116          // now delegate the processing to the section handler for the header ..
117          final FlowController flowController = getFlowController();
118          final ReportContext reportContext = flowController.getReportContext();
119          final LayoutControllerFactory layoutControllerFactory =
120              reportContext.getLayoutControllerFactory();
121    
122          final int idx = currentColumn % node.getContentCount();
123          final AutoTableLayoutController derived = (AutoTableLayoutController) clone();
124          return layoutControllerFactory.create
125              (flowController, node.getContentCell(idx), derived);
126        }
127    
128        // close the table-header section ..
129        final AttributeMap elementMap = LayoutControllerUtil.createEmptyMap
130            (AutoTableModule.AUTOTABLE_NAMESPACE, "data-row");
131        target.endElement(elementMap);
132    
133        final FlowController flowController =
134            getFlowController().performOperation(FlowControlOperation.ADVANCE);
135        final FlowController cfc = tryRepeatingCommit(flowController);
136        if (cfc != null)
137        {
138          // Go back to the beginning. We have made a commit, so the cursor points
139          // to the next row of data ..
140          final AutoTableLayoutController derived = (AutoTableLayoutController) clone();
141          derived.setFlowController(cfc);
142          derived.currentColumn = 0;
143          return derived;
144        }
145    
146        // Advance is impossible, that means we reached the end of the group or
147        // the end of the table ..
148        final AutoTableLayoutController derived = (AutoTableLayoutController) clone();
149        derived.currentColumn = 0;
150        derived.processingState = AutoTableLayoutController.HANDLING_FOOTER;
151        return derived;
152      }
153    
154      private LayoutController processFooter(final ReportTarget target)
155          throws ReportProcessingException, DataSourceException, ReportDataFactoryException
156      {
157        final AutoTableElement node = (AutoTableElement) getElement();
158        if (node.getFooterCount() == 0)
159        {
160          final AutoTableLayoutController derived = (AutoTableLayoutController) clone();
161          derived.currentColumn = 0;
162          derived.processingState = -1;
163          derived.setProcessingState(ElementLayoutController.FINISHING);
164          return derived;
165        }
166    
167        if (currentColumn == 0)
168        {
169          // Start a new table-header section ..
170          final AttributeMap elementMap = LayoutControllerUtil.createEmptyMap
171              (AutoTableModule.AUTOTABLE_NAMESPACE, "footer-row");
172          target.startElement(elementMap);
173        }
174    
175        if (currentColumn < columnCount)
176        {
177          // now delegate the processing to the section handler for the header ..
178          final FlowController flowController = getFlowController();
179          final ReportContext reportContext = flowController.getReportContext();
180          final LayoutControllerFactory layoutControllerFactory =
181              reportContext.getLayoutControllerFactory();
182    
183          final int idx = currentColumn % node.getFooterCount();
184          final AutoTableLayoutController derived = (AutoTableLayoutController) clone();
185          return layoutControllerFactory.create
186              (flowController, node.getFooterCell(idx), derived);
187        }
188    
189        // close the table-header section ..
190        final AttributeMap elementMap = LayoutControllerUtil.createEmptyMap
191            (AutoTableModule.AUTOTABLE_NAMESPACE, "footer-row");
192        target.endElement(elementMap);
193    
194        final AutoTableLayoutController derived = (AutoTableLayoutController) clone();
195        derived.currentColumn = 0;
196        derived.processingState = -1;
197        derived.setProcessingState(ElementLayoutController.FINISHING);
198        return derived;
199      }
200    
201      private LayoutController processHeader(final ReportTarget target)
202          throws ReportProcessingException, DataSourceException, ReportDataFactoryException
203      {
204        final AutoTableElement node = (AutoTableElement) getElement();
205        if (node.getHeaderCount() == 0)
206        {
207          final AutoTableLayoutController derived = (AutoTableLayoutController) clone();
208          derived.currentColumn = 0;
209          derived.processingState = AutoTableLayoutController.HANDLING_DATA;
210          return derived;
211        }
212    
213        if (currentColumn == 0)
214        {
215          // Start a new table-header section ..
216          final AttributeMap elementMap = LayoutControllerUtil.createEmptyMap
217              (AutoTableModule.AUTOTABLE_NAMESPACE, "header-row");
218          target.startElement(elementMap);
219        }
220    
221        if (currentColumn < columnCount)
222        {
223          // now delegate the processing to the section handler for the header ..
224          final FlowController flowController = getFlowController();
225          final ReportContext reportContext = flowController.getReportContext();
226          final LayoutControllerFactory layoutControllerFactory =
227              reportContext.getLayoutControllerFactory();
228    
229          final int idx = currentColumn % node.getHeaderCount();
230          final AutoTableLayoutController derived = (AutoTableLayoutController) clone();
231          return layoutControllerFactory.create
232              (flowController, node.getHeaderCell(idx), derived);
233        }
234    
235        // close the table-header section ..
236        final AttributeMap elementMap = LayoutControllerUtil.createEmptyMap
237            (AutoTableModule.AUTOTABLE_NAMESPACE, "header-row");
238        target.endElement(elementMap);
239    
240        final AutoTableLayoutController derived = (AutoTableLayoutController) clone();
241        derived.currentColumn = 0;
242        derived.processingState = AutoTableLayoutController.HANDLING_DATA;
243        return derived;
244      }
245    
246      /**
247       * Joins with a delegated process flow. This is generally called from a child flow and should *not* (I mean it!) be
248       * called from outside. If you do, you'll suffer.
249       *
250       * @param flowController the flow controller of the parent.
251       * @return the joined layout controller that incorperates all changes from the delegate.
252       */
253      public LayoutController join(final FlowController flowController)
254      {
255        final AutoTableLayoutController derived = (AutoTableLayoutController) clone();
256        derived.setFlowController(flowController);
257        derived.currentColumn += 1;
258        return derived;
259      }
260    
261      public int getCurrentColumn()
262      {
263        return currentColumn;
264      }
265    }