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: LayoutControllerUtil.java 3036 2007-07-24 19:15:05Z taqua $ 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.flow.layoutprocessor; 033 034 import java.util.Iterator; 035 import java.util.Map; 036 037 import org.jfree.layouting.input.style.CSSDeclarationRule; 038 import org.jfree.layouting.input.style.CSSStyleRule; 039 import org.jfree.layouting.input.style.StyleKey; 040 import org.jfree.layouting.input.style.StyleKeyRegistry; 041 import org.jfree.layouting.input.style.StyleRule; 042 import org.jfree.layouting.input.style.values.CSSValue; 043 import org.jfree.layouting.namespace.NamespaceDefinition; 044 import org.jfree.layouting.namespace.Namespaces; 045 import org.jfree.layouting.util.AttributeMap; 046 import org.jfree.report.DataSourceException; 047 import org.jfree.report.EmptyReportData; 048 import org.jfree.report.JFreeReportInfo; 049 import org.jfree.report.ReportDataFactoryException; 050 import org.jfree.report.ReportProcessingException; 051 import org.jfree.report.data.GlobalMasterRow; 052 import org.jfree.report.data.PrecomputeNode; 053 import org.jfree.report.data.PrecomputeNodeKey; 054 import org.jfree.report.data.PrecomputedValueRegistry; 055 import org.jfree.report.data.ReportDataRow; 056 import org.jfree.report.data.StaticExpressionRuntimeData; 057 import org.jfree.report.expressions.Expression; 058 import org.jfree.report.expressions.ExpressionRuntime; 059 import org.jfree.report.flow.EmptyReportTarget; 060 import org.jfree.report.flow.FlowControlOperation; 061 import org.jfree.report.flow.FlowController; 062 import org.jfree.report.flow.LayoutExpressionRuntime; 063 import org.jfree.report.flow.ReportContext; 064 import org.jfree.report.flow.ReportJob; 065 import org.jfree.report.flow.ReportStructureRoot; 066 import org.jfree.report.flow.ReportTarget; 067 import org.jfree.report.structure.Element; 068 import org.jfree.report.structure.Group; 069 import org.jfree.report.structure.Node; 070 import org.jfree.report.structure.Section; 071 import org.jfree.resourceloader.Resource; 072 import org.jfree.resourceloader.ResourceKey; 073 import org.jfree.resourceloader.ResourceManager; 074 075 /** 076 * Creation-Date: 24.11.2006, 15:01:22 077 * 078 * @author Thomas Morgner 079 */ 080 public class LayoutControllerUtil 081 { 082 public static final EmptyReportData EMPTY_REPORT_DATA = new EmptyReportData(); 083 084 private LayoutControllerUtil() 085 { 086 } 087 088 public static int findNodeInParent(final Section parentSection, 089 final Node n) 090 { 091 final Node[] nodes = parentSection.getNodeArray(); 092 for (int i = 0; i < nodes.length; i++) 093 { 094 final Node node = nodes[i]; 095 if (node == n) 096 { 097 return i; 098 } 099 } 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 }