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

   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: LayoutControllerUtil.java 3036 2007-07-24 19:15:05Z taqua $
  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 java.util.Iterator;
  35: import java.util.Map;
  36: 
  37: import org.jfree.layouting.input.style.CSSDeclarationRule;
  38: import org.jfree.layouting.input.style.CSSStyleRule;
  39: import org.jfree.layouting.input.style.StyleKey;
  40: import org.jfree.layouting.input.style.StyleKeyRegistry;
  41: import org.jfree.layouting.input.style.StyleRule;
  42: import org.jfree.layouting.input.style.values.CSSValue;
  43: import org.jfree.layouting.namespace.NamespaceDefinition;
  44: import org.jfree.layouting.namespace.Namespaces;
  45: import org.jfree.layouting.util.AttributeMap;
  46: import org.jfree.report.DataSourceException;
  47: import org.jfree.report.EmptyReportData;
  48: import org.jfree.report.JFreeReportInfo;
  49: import org.jfree.report.ReportDataFactoryException;
  50: import org.jfree.report.ReportProcessingException;
  51: import org.jfree.report.data.GlobalMasterRow;
  52: import org.jfree.report.data.PrecomputeNode;
  53: import org.jfree.report.data.PrecomputeNodeKey;
  54: import org.jfree.report.data.PrecomputedValueRegistry;
  55: import org.jfree.report.data.ReportDataRow;
  56: import org.jfree.report.data.StaticExpressionRuntimeData;
  57: import org.jfree.report.expressions.Expression;
  58: import org.jfree.report.expressions.ExpressionRuntime;
  59: import org.jfree.report.flow.EmptyReportTarget;
  60: import org.jfree.report.flow.FlowControlOperation;
  61: import org.jfree.report.flow.FlowController;
  62: import org.jfree.report.flow.LayoutExpressionRuntime;
  63: import org.jfree.report.flow.ReportContext;
  64: import org.jfree.report.flow.ReportJob;
  65: import org.jfree.report.flow.ReportStructureRoot;
  66: import org.jfree.report.flow.ReportTarget;
  67: import org.jfree.report.structure.Element;
  68: import org.jfree.report.structure.Group;
  69: import org.jfree.report.structure.Node;
  70: import org.jfree.report.structure.Section;
  71: import org.jfree.resourceloader.Resource;
  72: import org.jfree.resourceloader.ResourceKey;
  73: import org.jfree.resourceloader.ResourceManager;
  74: 
  75: /**
  76:  * Creation-Date: 24.11.2006, 15:01:22
  77:  *
  78:  * @author Thomas Morgner
  79:  */
  80: public class LayoutControllerUtil
  81: {
  82:   public static final EmptyReportData EMPTY_REPORT_DATA = new EmptyReportData();
  83: 
  84:   private LayoutControllerUtil()
  85:   {
  86:   }
  87: 
  88:   public static int findNodeInParent(final Section parentSection,
  89:                                      final Node n)
  90:   {
  91:     final Node[] nodes = parentSection.getNodeArray();
  92:     for (int i = 0; i < nodes.length; i++)
  93:     {
  94:       final Node node = nodes[i];
  95:       if (node == n)
  96:       {
  97:         return i;
  98:       }
  99:     }
 100:     return -1;
 101:   }
 102: 
 103:   public static StaticExpressionRuntimeData getStaticExpressionRuntime
 104:       (final FlowController fc,
 105:        final Object declaringParent)
 106:   {
 107:     final GlobalMasterRow dataRow = fc.getMasterRow();
 108:     final ReportJob reportJob = fc.getReportJob();
 109:     final StaticExpressionRuntimeData sdd = new StaticExpressionRuntimeData();
 110:     sdd.setData(dataRow.getReportDataRow().getReportData());
 111:     sdd.setDeclaringParent(declaringParent);
 112:     sdd.setConfiguration(reportJob.getConfiguration());
 113:     sdd.setReportContext(fc.getReportContext());
 114:     return sdd;
 115:   }
 116: 
 117: 
 118:   public static LayoutExpressionRuntime getExpressionRuntime
 119:       (final FlowController fc, final Object node)
 120:   {
 121:     final LayoutExpressionRuntime ler = new LayoutExpressionRuntime();
 122:     ler.setConfiguration(fc.getReportJob().getConfiguration());
 123:     ler.setReportContext(fc.getReportContext());
 124: 
 125:     final GlobalMasterRow masterRow = fc.getMasterRow();
 126:     ler.setDataRow(masterRow.getGlobalView());
 127: 
 128:     final ReportDataRow reportDataRow = masterRow.getReportDataRow();
 129:     if (reportDataRow == null)
 130:     {
 131:       ler.setData(EMPTY_REPORT_DATA);
 132:       ler.setCurrentRow(-1);
 133:     }
 134:     else
 135:     {
 136:       ler.setData(reportDataRow.getReportData());
 137:       ler.setCurrentRow(reportDataRow.getCursor());
 138:     }
 139: 
 140:     ler.setDeclaringParent(node);
 141:     return ler;
 142:   }
 143: 
 144: 
 145:   public static FlowController processFlowOperations(FlowController fc,
 146:                                                      final FlowControlOperation[] ops)
 147:       throws DataSourceException
 148:   {
 149:     for (int i = 0; i < ops.length; i++)
 150:     {
 151:       final FlowControlOperation op = ops[i];
 152:       fc = fc.performOperation(op);
 153:     }
 154:     return fc;
 155:   }
 156: 
 157: 
 158:   /**
 159:    * Checks, whether the current group should continue. If there is no group, we assume that we should continue. (This
 160:    * emulates the control-break-algorithm's default behaviour if testing an empty set of arguments.)
 161:    *
 162:    * @param fc   the current flow controller holding the data
 163:    * @param node the current node.
 164:    * @return true, if the group is finished and we should stop reiterating it, false if the group is not finished and we
 165:    *         can start iterating it again.
 166:    * @throws org.jfree.report.DataSourceException
 167:    *
 168:    */
 169:   public static boolean isGroupFinished(final FlowController fc,
 170:                                         final Node node)
 171:       throws DataSourceException
 172:   {
 173:     final Node nodeParent = node.getParent();
 174:     if (nodeParent == null)
 175:     {
 176:       return false;
 177:     }
 178:     Group group = nodeParent.getGroup();
 179:     if (group == null)
 180:     {
 181:       return false;
 182:     }
 183: 
 184:     // maybe we can move this state into the layoutstate itself so that
 185:     // we do not have to rebuild that crap all the time.
 186:     LayoutExpressionRuntime ler = null;
 187: 
 188:     // OK, now we are almost complete.
 189:     while (group != null)
 190:     {
 191:       if (ler == null)
 192:       {
 193:         ler = getExpressionRuntime(fc, node);
 194:       }
 195: 
 196:       ler.setDeclaringParent(group);
 197: 
 198:       final Expression groupingExpression = group.getGroupingExpression();
 199:       if (groupingExpression != null)
 200:       {
 201:         groupingExpression.setRuntime(ler);
 202:         final Object groupFinished;
 203:         try
 204:         {
 205:           groupFinished = groupingExpression.computeValue();
 206:         }
 207:         finally
 208:         {
 209:           groupingExpression.setRuntime(null);
 210:         }
 211: 
 212:         if (Boolean.TRUE.equals(groupFinished))
 213:         {
 214:           // If the group expression returns true, we should pack our belongings
 215:           // and stop with that process. The group is finished.
 216: 
 217:           // In Cobol, this would mean that one of the group-fields has changed.
 218:           return true;
 219:         }
 220:       }
 221: 
 222:       final Node parent = group.getParent();
 223:       if (parent == null)
 224:       {
 225:         group = null;
 226:       }
 227:       else
 228:       {
 229:         group = parent.getGroup();
 230:       }
 231:     }
 232:     return false;
 233:   }
 234: 
 235: 
 236:   private static void mergeDeclarationRule(final CSSDeclarationRule target,
 237:                                            final CSSDeclarationRule source)
 238:   {
 239:     final Iterator it = source.getPropertyKeys();
 240:     while (it.hasNext())
 241:     {
 242:       final StyleKey key = (StyleKey) it.next();
 243:       final CSSValue value = source.getPropertyCSSValue(key);
 244:       final boolean sourceImportant = source.isImportant(key);
 245:       final boolean targetImportant = target.isImportant(key);
 246:       if (targetImportant)
 247:       {
 248:         continue;
 249:       }
 250:       target.setPropertyValue(key, value);
 251:       target.setImportant(key, sourceImportant);
 252:     }
 253:   }
 254: 
 255:   private static CSSDeclarationRule processStyleAttribute
 256:       (final Object styleAttributeValue,
 257:        final Element node,
 258:        final ExpressionRuntime runtime,
 259:        CSSDeclarationRule targetRule)
 260:       throws DataSourceException
 261:   {
 262:     if (targetRule == null)
 263:     {
 264:       try
 265:       {
 266:         targetRule = (CSSDeclarationRule) node.getStyle().clone();
 267:       }
 268:       catch (CloneNotSupportedException e)
 269:       {
 270:         targetRule = new CSSStyleRule(null, null);
 271:       }
 272:     }
 273: 
 274: 
 275:     if (styleAttributeValue instanceof String)
 276:     {
 277:       // ugly, we have to parse that thing. Cant think of nothing
 278:       // worse than that.
 279:       final String styleText = (String) styleAttributeValue;
 280:       try
 281:       {
 282:         final ReportContext reportContext = runtime.getReportContext();
 283:         final ReportStructureRoot root = reportContext.getReportStructureRoot();
 284:         final ResourceKey baseResource = root.getBaseResource();
 285:         final ResourceManager resourceManager = root.getResourceManager();
 286: 
 287:         final byte[] bytes = styleText.getBytes("UTF-8");
 288:         final ResourceKey key = resourceManager.createKey(bytes);
 289:         final Resource resource = resourceManager.create
 290:             (key, baseResource, StyleRule.class);
 291: 
 292:         final CSSDeclarationRule parsedRule =
 293:             (CSSDeclarationRule) resource.getResource();
 294:         mergeDeclarationRule(targetRule, parsedRule);
 295:       }
 296:       catch (Exception e)
 297:       {
 298:         // ignore ..
 299:         e.printStackTrace();
 300:       }
 301:     }
 302:     else if (styleAttributeValue instanceof CSSStyleRule)
 303:     {
 304:       final CSSStyleRule styleRule =
 305:           (CSSStyleRule) styleAttributeValue;
 306:       mergeDeclarationRule(targetRule, styleRule);
 307:     }
 308: 
 309:     // ok, not lets fill in the stuff from the style expressions ..
 310:     final Map styleExpressions = node.getStyleExpressions();
 311:     final Iterator styleExIt = styleExpressions.entrySet().iterator();
 312: 
 313:     while (styleExIt.hasNext())
 314:     {
 315:       final Map.Entry entry = (Map.Entry) styleExIt.next();
 316:       final String name = (String) entry.getKey();
 317:       final Expression expression = (Expression) entry.getValue();
 318:       try
 319:       {
 320:         expression.setRuntime(runtime);
 321:         final Object value = expression.computeValue();
 322:         if (value instanceof CSSValue)
 323:         {
 324:           final CSSValue cssvalue = (CSSValue) value;
 325:           final StyleKey keyByName =
 326:               StyleKeyRegistry.getRegistry().findKeyByName(name);
 327:           if (keyByName != null)
 328:           {
 329:             targetRule.setPropertyValue(keyByName, cssvalue);
 330:           }
 331:           else
 332:           {
 333:             targetRule.setPropertyValueAsString(name, cssvalue.getCSSText());
 334:           }
 335:         }
 336:         else if (value != null)
 337:         {
 338:           targetRule.setPropertyValueAsString(name, String.valueOf(value));
 339:         }
 340:       }
 341:       finally
 342:       {
 343:         expression.setRuntime(null);
 344:       }
 345:     }
 346:     return targetRule;
 347:   }
 348: 
 349:   private static AttributeMap collectAttributes(final Element node,
 350:                                                 final ExpressionRuntime runtime)
 351:       throws DataSourceException
 352:   {
 353:     final AttributeMap attributes = node.getAttributeMap();
 354:     final AttributeMap attributeExpressions = node.getAttributeExpressionMap();
 355:     final String[] namespaces = attributeExpressions.getNameSpaces();
 356:     for (int i = 0; i < namespaces.length; i++)
 357:     {
 358:       final String namespace = namespaces[i];
 359:       final Map attrEx = attributeExpressions.getAttributes(namespace);
 360: 
 361:       final Iterator attributeExIt = attrEx.entrySet().iterator();
 362:       while (attributeExIt.hasNext())
 363:       {
 364:         final Map.Entry entry = (Map.Entry) attributeExIt.next();
 365:         final String name = (String) entry.getKey();
 366:         final Expression expression = (Expression) entry.getValue();
 367:         try
 368:         {
 369:           expression.setRuntime(runtime);
 370:           final Object value = expression.computeValue();
 371:           attributes.setAttribute(namespace, name, value);
 372:         }
 373:         finally
 374:         {
 375:           expression.setRuntime(null);
 376:         }
 377:       }
 378:     }
 379:     return attributes;
 380:   }
 381: 
 382:   public static AttributeMap processAttributes(final Element node,
 383:                                                final ReportTarget target,
 384:                                                final ExpressionRuntime runtime)
 385:       throws DataSourceException
 386:   {
 387:     final AttributeMap attributes = collectAttributes(node, runtime);
 388:     CSSDeclarationRule rule = null;
 389: 
 390:     final AttributeMap retval = new AttributeMap();
 391: 
 392:     final String[] attrNamespaces = attributes.getNameSpaces();
 393:     for (int i = 0; i < attrNamespaces.length; i++)
 394:     {
 395:       final String namespace = attrNamespaces[i];
 396:       final Map attributeMap = attributes.getAttributes(namespace);
 397:       if (attributeMap == null)
 398:       {
 399:         continue;
 400:       }
 401: 
 402:       final NamespaceDefinition nsDef = target.getNamespaceByUri(namespace);
 403:       final Iterator attributeIt = attributeMap.entrySet().iterator();
 404:       while (attributeIt.hasNext())
 405:       {
 406:         final Map.Entry entry = (Map.Entry) attributeIt.next();
 407:         final String key = (String) entry.getKey();
 408:         if (isStyleAttribute(nsDef, node.getType(), key))
 409:         {
 410:           final Object styleAttributeValue = entry.getValue();
 411:           rule = processStyleAttribute(styleAttributeValue, node, runtime,
 412:               rule);
 413:         }
 414:         else
 415:         {
 416:           retval.setAttribute(namespace, key, entry.getValue());
 417:         }
 418:       }
 419:     }
 420: 
 421:     // Just in case there was no style-attribute but there are style-expressions
 422:     if (rule == null)
 423:     {
 424:       rule = processStyleAttribute(null, node, runtime, rule);
 425:     }
 426: 
 427:     if (rule != null && rule.getSize() > 0)
 428:     {
 429:       retval.setAttribute(Namespaces.LIBLAYOUT_NAMESPACE, "style", rule);
 430:     }
 431: 
 432:     return retval;
 433:   }
 434: 
 435:   private static boolean isStyleAttribute(final NamespaceDefinition def,
 436:                                           final String elementName,
 437:                                           final String attrName)
 438:   {
 439:     if (def == null)
 440:     {
 441:       return false;
 442:     }
 443: 
 444:     final String[] styleAttr = def.getStyleAttribute(elementName);
 445:     for (int i = 0; i < styleAttr.length; i++)
 446:     {
 447:       final String styleAttrib = styleAttr[i];
 448:       if (attrName.equals(styleAttrib))
 449:       {
 450:         return true;
 451:       }
 452:     }
 453:     return false;
 454:   }
 455: 
 456:   public static AttributeMap createEmptyMap(final String namespace,
 457:                                             final String tagName)
 458:   {
 459:     final AttributeMap map = new AttributeMap();
 460:     map.setAttribute(JFreeReportInfo.REPORT_NAMESPACE,
 461:         Element.NAMESPACE_ATTRIBUTE, namespace);
 462:     map.setAttribute(JFreeReportInfo.REPORT_NAMESPACE,
 463:         Element.TYPE_ATTRIBUTE, tagName);
 464:     return map;
 465:   }
 466: 
 467: 
 468:   public static Object performPrecompute(final int expressionPosition,
 469:                                          final PrecomputeNodeKey nodeKey,
 470:                                          final LayoutController layoutController,
 471:                                          final FlowController flowController)
 472:       throws ReportProcessingException, ReportDataFactoryException,
 473:       DataSourceException
 474:   {
 475:     final FlowController fc = flowController.createPrecomputeInstance();
 476:     final PrecomputedValueRegistry pcvr = fc.getPrecomputedValueRegistry();
 477: 
 478:     pcvr.startElementPrecomputation(nodeKey);
 479: 
 480:     final LayoutController rootLc = layoutController.createPrecomputeInstance(fc);
 481:     final LayoutController rootParent = rootLc.getParent();
 482:     final ReportTarget target = new EmptyReportTarget(fc.getReportJob(), fc.getExportDescriptor());
 483: 
 484:     LayoutController lc = rootLc;
 485:     while (lc.isAdvanceable())
 486:     {
 487:       lc = lc.advance(target);
 488:       while (lc.isAdvanceable() == false && lc.getParent() != null)
 489:       {
 490:         final LayoutController parent = lc.getParent();
 491:         lc = parent.join(lc.getFlowController());
 492:       }
 493:     }
 494: 
 495:     target.commit();
 496:     final PrecomputeNode precomputeNode = pcvr.currentNode();
 497:     final Object functionResult = precomputeNode.getFunctionResult(expressionPosition);
 498:     pcvr.finishElementPrecomputation(nodeKey);
 499:     return functionResult;
 500: //    throw new IllegalStateException
 501: //        ("Ups - we did not get to the root parent again. This is awful and we cannot continue.");
 502:   }
 503: 
 504: 
 505:   public static LayoutController skipInvisibleElement(final LayoutController layoutController)
 506:       throws ReportProcessingException, ReportDataFactoryException, DataSourceException
 507:   {
 508:     final FlowController fc = layoutController.getFlowController();
 509:     final ReportTarget target = new EmptyReportTarget(fc.getReportJob(), fc.getExportDescriptor());
 510:     final LayoutController rootParent = layoutController.getParent();
 511: 
 512:     // Now start to iterate until the derived layout controller 'lc' that has this given parent
 513:     // wants to join.
 514:     LayoutController lc = layoutController;
 515:     while (lc.isAdvanceable())
 516:     {
 517:       lc = lc.advance(target);
 518:       while (lc.isAdvanceable() == false && lc.getParent() != null)
 519:       {
 520:         final LayoutController parent = lc.getParent();
 521:         lc = parent.join(lc.getFlowController());
 522:         if (parent == rootParent)
 523:         {
 524:           target.commit();
 525:           return lc;
 526:         }
 527:       }
 528:     }
 529:     target.commit();
 530:     throw new IllegalStateException
 531:         ("Ups - we did not get to the root parent again. This is awful and we cannot continue.");
 532: //    return lc;
 533:   }
 534: 
 535:   public static Object evaluateExpression(final FlowController flowController,
 536:                                           final Object declaringParent,
 537:                                           final Expression expression)
 538:       throws DataSourceException
 539:   {
 540:     final ExpressionRuntime runtime =
 541:         getExpressionRuntime(flowController, declaringParent);
 542: 
 543:     try
 544:     {
 545:       expression.setRuntime(runtime);
 546:       return expression.computeValue();
 547:     }
 548:     catch (DataSourceException dse)
 549:     {
 550:       throw dse;
 551:     }
 552:     catch (Exception e)
 553:     {
 554:       throw new DataSourceException("Failed to evaluate expression", e);
 555:     }
 556:     finally
 557:     {
 558:       expression.setRuntime(null);
 559:     }
 560:   }
 561: }