Source for org.jfree.report.flow.layoutprocessor.ElementLayoutController

   1: /**
   2:  * ========================================
   3:  * JFreeReport : a free Java report library
   4:  * ========================================
   5:  *
   6:  * Project Info:  http://reporting.pentaho.org/
   7:  *
   8:  * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
   9:  *
  10:  * This library is free software; you can redistribute it and/or modify it under the terms
  11:  * of the GNU Lesser General Public License as published by the Free Software Foundation;
  12:  * either version 2.1 of the License, or (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  15:  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16:  * See the GNU Lesser General Public License for more details.
  17:  *
  18:  * You should have received a copy of the GNU Lesser General Public License along with this
  19:  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  20:  * Boston, MA 02111-1307, USA.
  21:  *
  22:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  23:  * in the United States and other countries.]
  24:  *
  25:  * ------------
  26:  * $Id: ElementLayoutController.java 3525 2007-10-16 11:43:48Z tmorgner $
  27:  * ------------
  28:  * (C) Copyright 2000-2005, by Object Refinery Limited.
  29:  * (C) Copyright 2005-2007, by Pentaho Corporation.
  30:  */
  31: 
  32: package org.jfree.report.flow.layoutprocessor;
  33: 
  34: import org.jfree.report.DataSourceException;
  35: import org.jfree.report.ReportDataFactoryException;
  36: import org.jfree.report.ReportProcessingException;
  37: import org.jfree.report.data.ExpressionSlot;
  38: import org.jfree.report.data.PrecomputeNodeKey;
  39: import org.jfree.report.data.PrecomputedExpressionSlot;
  40: import org.jfree.report.data.PrecomputedValueRegistry;
  41: import org.jfree.report.data.RunningExpressionSlot;
  42: import org.jfree.report.data.StaticExpressionRuntimeData;
  43: import org.jfree.report.expressions.Expression;
  44: import org.jfree.report.flow.FlowControlOperation;
  45: import org.jfree.report.flow.FlowController;
  46: import org.jfree.report.flow.ReportTarget;
  47: import org.jfree.report.flow.LayoutExpressionRuntime;
  48: import org.jfree.report.structure.Element;
  49: import org.jfree.util.Log;
  50: import org.jfree.layouting.util.AttributeMap;
  51: 
  52: /**
  53:  * Creation-Date: 24.11.2006, 13:56:30
  54:  *
  55:  * @author Thomas Morgner
  56:  */
  57: public abstract class ElementLayoutController
  58:     implements LayoutController
  59: {
  60:   protected static class ElementPrecomputeKey implements PrecomputeNodeKey
  61:   {
  62:     private String name;
  63:     private String id;
  64:     private String namespace;
  65:     private String tagName;
  66: 
  67:     protected ElementPrecomputeKey(final Element element)
  68:     {
  69:       this.name = element.getName();
  70:       this.tagName = element.getType();
  71:       this.namespace = element.getNamespace();
  72:       this.id = element.getId();
  73:     }
  74: 
  75:     public boolean equals(final Object obj)
  76:     {
  77:       if (this == obj)
  78:       {
  79:         return true;
  80:       }
  81:       if (obj == null || getClass() != obj.getClass())
  82:       {
  83:         return false;
  84:       }
  85: 
  86:       final ElementPrecomputeKey that = (ElementPrecomputeKey) obj;
  87: 
  88:       if (id != null ? !id.equals(that.id) : that.id != null)
  89:       {
  90:         return false;
  91:       }
  92:       if (name != null ? !name.equals(that.name) : that.name != null)
  93:       {
  94:         return false;
  95:       }
  96:       if (namespace != null ? !namespace.equals(
  97:           that.namespace) : that.namespace != null)
  98:       {
  99:         return false;
 100:       }
 101:       if (tagName != null ? !tagName.equals(
 102:           that.tagName) : that.tagName != null)
 103:       {
 104:         return false;
 105:       }
 106: 
 107:       return true;
 108:     }
 109: 
 110:     public int hashCode()
 111:     {
 112:       int result = (name != null ? name.hashCode() : 0);
 113:       result = 29 * result + (id != null ? id.hashCode() : 0);
 114:       result = 29 * result + (namespace != null ? namespace.hashCode() : 0);
 115:       result = 29 * result + (tagName != null ? tagName.hashCode() : 0);
 116:       return result;
 117:     }
 118: 
 119:     public boolean equals(final PrecomputeNodeKey otherKey)
 120:     {
 121:       return false;
 122:     }
 123:   }
 124: 
 125:   public static final int NOT_STARTED = 0;
 126:   public static final int OPENED = 1;
 127:   public static final int WAITING_FOR_JOIN = 2;
 128:   public static final int FINISHING = 3;
 129:   //public static final int JOINING = 4;
 130:   public static final int FINISHED = 4;
 131: 
 132:   private int processingState;
 133:   private FlowController flowController;
 134:   private Element element;
 135:   private LayoutController parent;
 136:   private boolean precomputing;
 137:   private AttributeMap attributeMap;
 138:   private int expressionsCount;
 139:   private int iterationCount;
 140: 
 141:   protected ElementLayoutController()
 142:   {
 143:     this.processingState = ElementLayoutController.NOT_STARTED;
 144:   }
 145: 
 146: 
 147:   public String toString()
 148:   {
 149:     return "ElementLayoutController{" +
 150:         "processingState=" + processingState +
 151:         ", element=" + element +
 152:         ", precomputing=" + precomputing +
 153:         ", expressionsCount=" + expressionsCount +
 154:         ", iterationCount=" + iterationCount +
 155:         '}';
 156:   }
 157: 
 158:   /**
 159:    * Retrieves the parent of this layout controller. This allows childs to query
 160:    * their context.
 161:    *
 162:    * @return the layout controller's parent to <code>null</code> if there is no
 163:    * parent.
 164:    */
 165:   public LayoutController getParent()
 166:   {
 167:     return parent;
 168:   }
 169: 
 170: 
 171:   /**
 172:    * Initializes the layout controller. This method is called exactly once. It
 173:    * is the creators responsibility to call this method.
 174:    * <p/>
 175:    * Calling initialize after the first advance must result in a
 176:    * IllegalStateException.
 177:    *
 178:    * @param node           the currently processed object or layout node.
 179:    * @param flowController the current flow controller.
 180:    * @param parent         the parent layout controller that was responsible for
 181:    *                       instantiating this controller.
 182:    * @throws DataSourceException        if there was a problem reading data from
 183:    *                                    the datasource.
 184:    * @throws ReportProcessingException  if there was a general problem during
 185:    *                                    the report processing.
 186:    * @throws ReportDataFactoryException if a query failed.
 187:    */
 188:   public void initialize(final Object node,
 189:                          final FlowController flowController,
 190:                          final LayoutController parent)
 191:       throws DataSourceException, ReportDataFactoryException,
 192:       ReportProcessingException
 193:   {
 194: 
 195:     if (processingState != ElementLayoutController.NOT_STARTED)
 196:     {
 197:       throw new IllegalStateException();
 198:     }
 199: 
 200:     this.element = (Element) node;
 201:     this.flowController = flowController;
 202:     this.parent = parent;
 203:     this.iterationCount = -1;
 204:   }
 205: 
 206:   /**
 207:    * Advances the layout controller to the next state. This method delegates the
 208:    * call to one of the following methods: <ul> <li>{@link
 209:    * #startElement(org.jfree.report.flow.ReportTarget)} <li>{@link
 210:    * #processContent(org.jfree.report.flow.ReportTarget)} <li>{@link
 211:    * #finishElement(org.jfree.report.flow.ReportTarget)} </ul>
 212:    *
 213:    * @param target the report target that receives generated events.
 214:    * @return the new layout controller instance representing the new state.
 215:    *
 216:    * @throws DataSourceException        if there was a problem reading data from
 217:    *                                    the datasource.
 218:    * @throws ReportProcessingException  if there was a general problem during
 219:    *                                    the report processing.
 220:    * @throws ReportDataFactoryException if a query failed.
 221:    */
 222:   public final LayoutController advance(final ReportTarget target)
 223:       throws DataSourceException, ReportProcessingException,
 224:       ReportDataFactoryException
 225:   {
 226:     final int processingState = getProcessingState();
 227:     switch (processingState)
 228:     {
 229:       case ElementLayoutController.NOT_STARTED:
 230:         return startElement(target);
 231:       case ElementLayoutController.OPENED:
 232:         return processContent(target);
 233:       case ElementLayoutController.FINISHING:
 234:         return finishElement(target);
 235: //      case ElementLayoutController.JOINING:
 236: //        return joinWithParent();
 237:       default:
 238:         throw new IllegalStateException();
 239:     }
 240:   }
 241: 
 242:   /**
 243:    * This method is called for each newly instantiated layout controller. The
 244:    * returned layout controller instance should have a processing state of
 245:    * either 'OPEN' or 'FINISHING' depending on whether there is any content or
 246:    * any child nodes to process.
 247:    *
 248:    * @param target the report target that receives generated events.
 249:    * @return the new layout controller instance representing the new state.
 250:    *
 251:    * @throws DataSourceException        if there was a problem reading data from
 252:    *                                    the datasource.
 253:    * @throws ReportProcessingException  if there was a general problem during
 254:    *                                    the report processing.
 255:    * @throws ReportDataFactoryException if a query failed.
 256:    */
 257:   protected LayoutController startElement(final ReportTarget target)
 258:       throws DataSourceException, ReportProcessingException,
 259:       ReportDataFactoryException
 260:   {
 261:     final Element s = getElement();
 262: 
 263:     FlowController fc = getFlowController();
 264:     // Step 3: Add the expressions. Any expressions defined for the subreport
 265:     // will work on the queried dataset.
 266:     fc = startData(target, fc);
 267: 
 268:     final Expression[] expressions = s.getExpressions();
 269:     fc = performElementPrecomputation(expressions, fc);
 270: 
 271:     if (s.isVirtual() == false)
 272:     {
 273:       attributeMap = computeAttributes(fc, s, target);
 274:       target.startElement(attributeMap);
 275:     }
 276: 
 277:     final ElementLayoutController derived = (ElementLayoutController) clone();
 278:     derived.setProcessingState(ElementLayoutController.OPENED);
 279:     derived.setFlowController(fc);
 280:     derived.expressionsCount = expressions.length;
 281:     derived.attributeMap = attributeMap;
 282:     derived.iterationCount += 1;
 283:     return derived;
 284:   }
 285: 
 286:   public AttributeMap getAttributeMap()
 287:   {
 288:     return attributeMap;
 289:   }
 290: 
 291:   public int getExpressionsCount()
 292:   {
 293:     return expressionsCount;
 294:   }
 295: 
 296:   public int getIterationCount()
 297:   {
 298:     return iterationCount;
 299:   }
 300: 
 301: 
 302:   protected FlowController startData(final ReportTarget target,
 303:                                      final FlowController fc)
 304:       throws DataSourceException, ReportProcessingException,
 305:       ReportDataFactoryException
 306:   {
 307:     return fc;
 308:   }
 309: 
 310:   protected AttributeMap computeAttributes(final FlowController fc,
 311:                                            final Element element,
 312:                                            final ReportTarget target)
 313:       throws DataSourceException
 314:   {
 315:     final LayoutExpressionRuntime ler =
 316:         LayoutControllerUtil.getExpressionRuntime(fc, element);
 317:     return LayoutControllerUtil.processAttributes(element, target, ler);
 318:   }
 319: 
 320: 
 321:   /**
 322:    * Processes any content in this element. This method is called when the
 323:    * processing state is 'OPENED'. The returned layout controller will retain
 324:    * the 'OPENED' state as long as there is more content available. Once all
 325:    * content has been processed, the returned layout controller should carry a
 326:    * 'FINISHED' state.
 327:    *
 328:    * @param target the report target that receives generated events.
 329:    * @return the new layout controller instance representing the new state.
 330:    *
 331:    * @throws DataSourceException        if there was a problem reading data from
 332:    *                                    the datasource.
 333:    * @throws ReportProcessingException  if there was a general problem during
 334:    *                                    the report processing.
 335:    * @throws ReportDataFactoryException if a query failed.
 336:    */
 337:   protected abstract LayoutController processContent(final ReportTarget target)
 338:       throws DataSourceException, ReportProcessingException,
 339:       ReportDataFactoryException;
 340: 
 341:   /**
 342:    * Finishes the processing of this element. This method is called when the
 343:    * processing state is 'FINISHING'. The element should be closed now and all
 344:    * privatly owned resources should be freed. If the element has a parent, it
 345:    * would be time to join up with the parent now, else the processing state
 346:    * should be set to 'FINISHED'.
 347:    *
 348:    * @param target the report target that receives generated events.
 349:    * @return the new layout controller instance representing the new state.
 350:    *
 351:    * @throws DataSourceException       if there was a problem reading data from
 352:    *                                   the datasource.
 353:    * @throws ReportProcessingException if there was a general problem during the
 354:    *                                   report processing.
 355:    * @throws ReportDataFactoryException if there was an error trying query data.
 356:    */
 357:   protected LayoutController finishElement(final ReportTarget target)
 358:       throws ReportProcessingException, DataSourceException,
 359:       ReportDataFactoryException
 360:   {
 361:     final FlowController fc = handleDefaultEndElement(target);
 362:     final ElementLayoutController derived = (ElementLayoutController) clone();
 363:     derived.setProcessingState(ElementLayoutController.FINISHED);
 364:     derived.setFlowController(fc);
 365:     return derived;
 366:   }
 367: 
 368:   protected FlowController handleDefaultEndElement (final ReportTarget target)
 369:       throws ReportProcessingException, DataSourceException,
 370:       ReportDataFactoryException
 371:   {
 372:     final Element e = getElement();
 373:     // Step 1: call End Element
 374:     if (e.isVirtual() == false)
 375:     {
 376:       target.endElement(getAttributeMap());
 377:     }
 378: 
 379:     FlowController fc = getFlowController();
 380:     final PrecomputedValueRegistry pcvr =
 381:         fc.getPrecomputedValueRegistry();
 382:     // Step 2: Remove the expressions of this element
 383:     final int expressionsCount = getExpressionsCount();
 384:     if (expressionsCount != 0)
 385:     {
 386:       final ExpressionSlot[] activeExpressions = fc.getActiveExpressions();
 387:       for (int i = activeExpressions.length - expressionsCount; i < activeExpressions.length; i++)
 388:       {
 389:         final ExpressionSlot slot = activeExpressions[i];
 390:         pcvr.addFunction(slot.getName(), slot.getValue());
 391:       }
 392:       fc = fc.deactivateExpressions();
 393:     }
 394: 
 395:     if (isPrecomputing() == false)
 396:     {
 397:       pcvr.finishElement(new ElementPrecomputeKey(e));
 398:     }
 399: 
 400:     return fc;
 401:   }
 402: //
 403: //  /**
 404: //   * Joins the layout controller with the parent. This simply calls
 405: //   * {@link #join(org.jfree.report.flow.FlowController)} on the parent. A join
 406: //   * operation is necessary to propagate changes in the flow-controller to the
 407: //   * parent for further processing.
 408: //   *
 409: //   * @return the joined parent.
 410: //   * @throws IllegalStateException if this layout controller has no parent.
 411: //   */
 412: //  protected LayoutController joinWithParent()
 413: //      throws ReportProcessingException, ReportDataFactoryException,
 414: //      DataSourceException
 415: //  {
 416: //    final LayoutController parent = getParent();
 417: //    if (parent == null)
 418: //    {
 419: //      // skip to the next step ..
 420: //      throw new IllegalStateException("There is no parent to join with. " +
 421: //                                      "This should not happen in a sane environment!");
 422: //    }
 423: //
 424: //    return parent.join(getFlowController());
 425: //  }
 426: 
 427:   public boolean isAdvanceable()
 428:   {
 429:     return processingState != ElementLayoutController.FINISHED;
 430:   }
 431: 
 432:   public Element getElement()
 433:   {
 434:     return element;
 435:   }
 436: 
 437:   public FlowController getFlowController()
 438:   {
 439:     return flowController;
 440:   }
 441: 
 442:   public int getProcessingState()
 443:   {
 444:     return processingState;
 445:   }
 446: 
 447:   public void setProcessingState(final int processingState)
 448:   {
 449:     this.processingState = processingState;
 450:   }
 451: 
 452:   public void setFlowController(final FlowController flowController)
 453:   {
 454:     this.flowController = flowController;
 455:   }
 456: 
 457:   public void setParent(final LayoutController parent)
 458:   {
 459:     this.parent = parent;
 460:   }
 461: 
 462:   public Object clone()
 463:   {
 464:     try
 465:     {
 466:       return super.clone();
 467:     }
 468:     catch (CloneNotSupportedException e)
 469:     {
 470:       Log.error("Clone not supported: ", e);
 471:       throw new IllegalStateException("Clone must be supported.");
 472:     }
 473:   }
 474: 
 475:   public boolean isPrecomputing()
 476:   {
 477:     return precomputing;
 478:   }
 479: 
 480:   protected FlowController performElementPrecomputation(
 481:       final Expression[] expressions,
 482:       FlowController fc)
 483:       throws ReportProcessingException, ReportDataFactoryException,
 484:       DataSourceException
 485:   {
 486:     final Element element = getElement();
 487:     final PrecomputedValueRegistry pcvr = fc.getPrecomputedValueRegistry();
 488:     if (isPrecomputing() == false)
 489:     {
 490:       pcvr.startElement(new ElementPrecomputeKey(element));
 491:     }
 492: 
 493:     if (expressions.length > 0)
 494:     {
 495:       final ExpressionSlot[] slots = new ExpressionSlot[expressions.length];
 496:       final StaticExpressionRuntimeData runtimeData =
 497:           LayoutControllerUtil.getStaticExpressionRuntime(fc, element);
 498: 
 499:       for (int i = 0; i < expressions.length; i++)
 500:       {
 501:         final Expression expression = expressions[i];
 502:         if (isPrecomputing() == false && expression.isPrecompute())
 503:         {
 504:           // ok, we have to precompute the expression's value. For that
 505:           // we fork a new layout process, compute the value and then come
 506:           // back with the result.
 507:           final Object value = LayoutControllerUtil.performPrecompute
 508:               (i, new ElementPrecomputeKey(element),
 509:                   this, getFlowController());
 510:           slots[i] = new PrecomputedExpressionSlot(expression.getName(), value,
 511:               expression.isPreserve());
 512:         }
 513:         else
 514:         {
 515:           // thats a bit easier; we dont have to do anything special ..
 516:           slots[i] = new RunningExpressionSlot(expression, runtimeData,
 517:               pcvr.currentNode());
 518:         }
 519:       }
 520: 
 521:       fc = fc.activateExpressions(slots);
 522:     }
 523:     return fc;
 524:   }
 525: 
 526: 
 527:   protected FlowController tryRepeatingCommit(final FlowController fc)
 528:       throws DataSourceException
 529:   {
 530:     if (isPrecomputing() == false)
 531:     {
 532:       // ok, the user wanted us to repeat. So we repeat if the group in which
 533:       // we are in, is not closed (and at least one advance has been fired
 534:       // since the last repeat request [to prevent infinite loops]) ...
 535:       final boolean advanceRequested = fc.isAdvanceRequested();
 536:       final boolean advanceable = fc.getMasterRow().isAdvanceable();
 537:       if (advanceable && advanceRequested)
 538:       {
 539:         // we check against the commited target; But we will not use the
 540:         // commited target if the group is no longer active...
 541:         final FlowController cfc =
 542:             fc.performOperation(FlowControlOperation.COMMIT);
 543:         final boolean groupFinished =
 544:             LayoutControllerUtil.isGroupFinished(cfc, getElement());
 545:         if (groupFinished == false)
 546:         {
 547:           return cfc;
 548:         }
 549:       }
 550:     }
 551:     return null;
 552:   }
 553: 
 554: 
 555:   /**
 556:    * Derives a copy of this controller that is suitable to perform a
 557:    * precomputation.
 558:    *
 559:    * @param fc
 560:    * @return
 561:    */
 562:   public LayoutController createPrecomputeInstance(final FlowController fc)
 563:   {
 564:     final ElementLayoutController lc = (ElementLayoutController) clone();
 565:     lc.setFlowController(fc);
 566:     lc.setParent(null);
 567:     lc.precomputing = true;
 568:     return lc;
 569:   }
 570: 
 571: 
 572:   public Object getNode()
 573:   {
 574:     return getElement();
 575:   }
 576: }