1 28 package net.sf.jasperreports.engine.fill; 29 30 import java.io.IOException ; 31 import java.util.ArrayList ; 32 import java.util.Comparator ; 33 import java.util.HashMap ; 34 import java.util.Iterator ; 35 import java.util.List ; 36 import java.util.Map ; 37 import java.util.ResourceBundle ; 38 39 import net.sf.jasperreports.crosstabs.JRCellContents; 40 import net.sf.jasperreports.crosstabs.JRCrosstab; 41 import net.sf.jasperreports.crosstabs.JRCrosstabBucket; 42 import net.sf.jasperreports.crosstabs.JRCrosstabCell; 43 import net.sf.jasperreports.crosstabs.JRCrosstabColumnGroup; 44 import net.sf.jasperreports.crosstabs.JRCrosstabDataset; 45 import net.sf.jasperreports.crosstabs.JRCrosstabGroup; 46 import net.sf.jasperreports.crosstabs.JRCrosstabMeasure; 47 import net.sf.jasperreports.crosstabs.JRCrosstabParameter; 48 import net.sf.jasperreports.crosstabs.JRCrosstabRowGroup; 49 import net.sf.jasperreports.crosstabs.base.JRBaseCrosstab; 50 import net.sf.jasperreports.crosstabs.design.JRDesignCrosstab; 51 import net.sf.jasperreports.crosstabs.fill.JRCrosstabExpressionEvaluator; 52 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabCell; 53 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabColumnGroup; 54 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabGroup; 55 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabMeasure; 56 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabParameter; 57 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabRowGroup; 58 import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition; 59 import net.sf.jasperreports.crosstabs.fill.calculation.BucketingService; 60 import net.sf.jasperreports.crosstabs.fill.calculation.CrosstabCell; 61 import net.sf.jasperreports.crosstabs.fill.calculation.HeaderCell; 62 import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition; 63 import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition.Bucket; 64 import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition.MeasureValue; 65 import net.sf.jasperreports.engine.JRAbstractObjectFactory; 66 import net.sf.jasperreports.engine.JRChild; 67 import net.sf.jasperreports.engine.JRElement; 68 import net.sf.jasperreports.engine.JRException; 69 import net.sf.jasperreports.engine.JRExpression; 70 import net.sf.jasperreports.engine.JRExpressionChunk; 71 import net.sf.jasperreports.engine.JRExpressionCollector; 72 import net.sf.jasperreports.engine.JRGraphicElement; 73 import net.sf.jasperreports.engine.JRParameter; 74 import net.sf.jasperreports.engine.JRPrintElement; 75 import net.sf.jasperreports.engine.JRPrintFrame; 76 import net.sf.jasperreports.engine.JRPrintRectangle; 77 import net.sf.jasperreports.engine.JRRuntimeException; 78 import net.sf.jasperreports.engine.JRStyle; 79 import net.sf.jasperreports.engine.JRVariable; 80 import net.sf.jasperreports.engine.JasperReport; 81 import net.sf.jasperreports.engine.design.JRDefaultCompiler; 82 import net.sf.jasperreports.engine.design.JRDesignRectangle; 83 import net.sf.jasperreports.engine.util.JRStyleResolver; 84 import net.sf.jasperreports.engine.xml.JRXmlWriter; 85 86 import org.jfree.data.general.Dataset; 87 88 94 public class JRFillCrosstab extends JRFillElement implements JRCrosstab 95 { 96 final protected JRCrosstab parentCrosstab; 97 98 protected JRFillCrosstabDataset dataset; 99 100 protected JRFillCrosstabRowGroup[] rowGroups; 101 102 protected Map rowGroupsMap; 103 104 protected JRFillCrosstabColumnGroup[] columnGroups; 105 106 protected Map columnGroupsMap; 107 108 protected JRFillCrosstabMeasure[] measures; 109 110 protected BucketingService bucketingService; 111 112 protected JRFillVariable[] variables; 113 114 protected Map variablesMap; 115 116 protected JRFillVariable[][][] totalVariables; 117 protected boolean[][] retrieveTotal; 118 119 protected JRFillCrosstabParameter[] parameters; 120 121 protected Map parametersMap; 122 123 protected JRCrosstabExpressionEvaluator crosstabEvaluator; 124 125 protected JRFillCrosstabCell[][] crossCells; 126 protected JRFillCellContents headerCell; 127 protected JRFillCellContents whenNoDataCell; 128 129 protected boolean hasData; 130 protected HeaderCell[][] columnHeadersData; 131 protected HeaderCell[][] rowHeadersData; 132 protected CrosstabCell[][] cellData; 133 protected MeasureValue[] grandTotals; 134 135 private boolean percentage; 136 137 private CrosstabFiller crosstabFiller; 138 139 public JRFillCrosstab(JRBaseFiller filler, JRCrosstab crosstab, JRFillObjectFactory factory) 140 { 141 super(filler, crosstab, factory); 142 143 parentCrosstab = crosstab; 144 145 loadEvaluator(filler.getJasperReport()); 146 147 JRFillObjectFactory crosstabFactory = new JRFillObjectFactory(filler, crosstabEvaluator); 148 149 headerCell = crosstabFactory.getCell(crosstab.getHeaderCell()); 150 151 copyRowGroups(crosstab, crosstabFactory); 152 copyColumnGroups(crosstab, crosstabFactory); 153 154 copyMeasures(crosstab, crosstabFactory); 155 copyCells(crosstab, crosstabFactory); 156 whenNoDataCell = crosstabFactory.getCell(crosstab.getWhenNoDataCell()); 157 158 dataset = factory.getCrosstabDataset(crosstab.getDataset(), this); 159 160 copyParameters(crosstab, factory); 161 copyVariables(crosstab, crosstabFactory); 162 163 crosstabFiller = new CrosstabFiller(); 164 } 165 166 169 public byte getMode() 170 { 171 return JRStyleResolver.getMode(this, MODE_TRANSPARENT); 172 } 173 174 private void copyRowGroups(JRCrosstab crosstab, JRFillObjectFactory factory) 175 { 176 JRCrosstabRowGroup[] groups = crosstab.getRowGroups(); 177 rowGroups = new JRFillCrosstabRowGroup[groups.length]; 178 rowGroupsMap = new HashMap (); 179 for (int i = 0; i < groups.length; ++i) 180 { 181 JRFillCrosstabRowGroup group = factory.getCrosstabRowGroup(groups[i]); 182 group.getFillHeader().setVerticalPositionType(groups[i].getPosition()); 183 184 rowGroups[i] = group; 185 rowGroupsMap.put(group.getName(), new Integer (i)); 186 } 187 } 188 189 private void copyColumnGroups(JRCrosstab crosstab, JRFillObjectFactory factory) 190 { 191 JRCrosstabColumnGroup[] groups = crosstab.getColumnGroups(); 192 columnGroups = new JRFillCrosstabColumnGroup[groups.length]; 193 columnGroupsMap = new HashMap (); 194 for (int i = 0; i < groups.length; ++i) 195 { 196 JRFillCrosstabColumnGroup group = factory.getCrosstabColumnGroup(groups[i]); 197 columnGroups[i] = group; 198 columnGroupsMap.put(group.getName(), new Integer (i)); 199 } 200 } 201 202 private void copyMeasures(JRCrosstab crosstab, JRFillObjectFactory factory) 203 { 204 JRCrosstabMeasure[] crossMeasures = crosstab.getMeasures(); 205 measures = new JRFillCrosstabMeasure[crossMeasures.length]; 206 for (int i = 0; i < crossMeasures.length; i++) 207 { 208 measures[i] = factory.getCrosstabMeasure(crossMeasures[i]); 209 } 210 } 211 212 private void copyParameters(JRCrosstab crosstab, JRFillObjectFactory factory) 213 { 214 JRCrosstabParameter[] crossParams = crosstab.getParameters(); 215 parameters = new JRFillCrosstabParameter[crossParams.length]; 216 parametersMap = new HashMap (); 217 for (int i = 0; i < crossParams.length; i++) 218 { 219 parameters[i] = factory.getCrosstabParameter(crossParams[i]); 220 parametersMap.put(parameters[i].getName(), parameters[i]); 221 } 222 } 223 224 private void copyCells(JRCrosstab crosstab, JRFillObjectFactory factory) 225 { 226 JRCrosstabCell[][] crosstabCells = crosstab.getCells(); 227 crossCells = new JRFillCrosstabCell[rowGroups.length + 1][columnGroups.length + 1]; 228 for (int i = 0; i <= rowGroups.length; ++i) 229 { 230 for (int j = 0; j <= columnGroups.length; ++j) 231 { 232 if (crosstabCells[i][j] != null) 233 { 234 crossCells[i][j] = factory.getCrosstabCell(crosstabCells[i][j]); 235 } 236 } 237 } 238 } 239 240 private void copyVariables(JRCrosstab crosstab, JRFillObjectFactory factory) 241 { 242 JRVariable[] vars = crosstab.getVariables(); 243 variables = new JRFillVariable[vars.length]; 244 variablesMap = new HashMap (); 245 for (int i = 0; i < variables.length; i++) 246 { 247 variables[i] = factory.getVariable(vars[i]); 248 variablesMap.put(variables[i].getName(), variables[i]); 249 } 250 251 Map totalVarPos = new HashMap (); 252 totalVariables = new JRFillVariable[rowGroups.length + 1][columnGroups.length + 1][measures.length]; 253 for (int row = 0; row <= rowGroups.length; ++row) 254 { 255 JRCrosstabRowGroup rowGroup = row == rowGroups.length ? null : rowGroups[row]; 256 for (int col = 0; col <= columnGroups.length; ++col) 257 { 258 JRCrosstabColumnGroup colGroup = col == columnGroups.length ? null : columnGroups[col]; 259 260 if (row < rowGroups.length || col < columnGroups.length) 261 { 262 for (int m = 0; m < measures.length; m++) 263 { 264 String totalVariableName = JRDesignCrosstab.getTotalVariableName(measures[m], rowGroup, colGroup); 265 totalVariables[row][col][m] = (JRFillVariable) variablesMap.get(totalVariableName); 266 totalVarPos.put(totalVariableName, new int[]{row, col}); 267 } 268 } 269 } 270 } 271 272 retrieveTotal = new boolean[rowGroups.length + 1][columnGroups.length + 1]; 273 274 List expressions = JRExpressionCollector.collectExpressions(filler.getJasperReport(), crosstab); 276 for (Iterator iter = expressions.iterator(); iter.hasNext();) 277 { 278 JRExpression expression = (JRExpression) iter.next(); 279 JRExpressionChunk[] chunks = expression.getChunks(); 280 if (chunks != null) 281 { 282 for (int i = 0; i < chunks.length; i++) 283 { 284 JRExpressionChunk chunk = chunks[i]; 285 if (chunk.getType() == JRExpressionChunk.TYPE_VARIABLE) 286 { 287 String varName = chunk.getText(); 288 int[] pos = (int[]) totalVarPos.get(varName); 289 if (pos != null) 290 { 291 retrieveTotal[pos[0]][pos[1]] = true; 292 } 293 } 294 } 295 } 296 } 297 } 298 299 protected void loadEvaluator(JasperReport jasperReport) 300 { 301 try 302 { 303 JREvaluator evaluator = JRDefaultCompiler.getInstance().loadEvaluator(jasperReport, parentCrosstab); 304 crosstabEvaluator = new JRCrosstabExpressionEvaluator(evaluator); 305 } 306 catch (JRException e) 307 { 308 throw new JRRuntimeException("Could not load evaluator for crosstab.", e); 309 } 310 } 311 312 private BucketingService createService(byte evaluation) throws JRException 313 { 314 List rowBuckets = new ArrayList (rowGroups.length); 315 for (int i = 0; i < rowGroups.length; ++i) 316 { 317 rowBuckets.add(createServiceBucket(rowGroups[i], evaluation)); 318 } 319 320 List colBuckets = new ArrayList (columnGroups.length); 321 for (int i = 0; i < columnGroups.length; ++i) 322 { 323 colBuckets.add(createServiceBucket(columnGroups[i], evaluation)); 324 } 325 326 percentage = false; 327 List measureList = new ArrayList (measures.length); 328 for (int i = 0; i < measures.length; ++i) 329 { 330 measureList.add(createServiceMeasure(measures[i])); 331 percentage |= measures[i].getPercentageOfType() == JRCrosstabMeasure.PERCENTAGE_TYPE_GRAND_TOTAL; 332 } 333 334 if (percentage) 335 { 336 ((BucketDefinition) rowBuckets.get(0)).setComputeTotal(); 337 ((BucketDefinition) colBuckets.get(0)).setComputeTotal(); 338 } 339 340 return new BucketingService(rowBuckets, colBuckets, measureList, dataset.isDataPreSorted(), retrieveTotal); 341 } 342 343 private BucketDefinition createServiceBucket(JRCrosstabGroup group, byte evaluation) throws JRException 344 { 345 JRCrosstabBucket bucket = group.getBucket(); 346 347 Comparator comparator = null; 348 JRExpression comparatorExpression = bucket.getComparatorExpression(); 349 if (comparatorExpression != null) 350 { 351 comparator = (Comparator ) evaluateExpression(comparatorExpression, evaluation); 352 } 353 354 byte totalPosition = group.getTotalPosition(); 355 return new BucketDefinition(bucket.getExpression().getValueClass(), comparator, bucket.getOrder(), totalPosition); 356 } 357 358 private MeasureDefinition createServiceMeasure(JRFillCrosstabMeasure measure) 359 { 360 return new MeasureDefinition( 361 measure.getValueClass(), 362 measure.getCalculation(), 363 measure.getIncrementerFactory()); 364 } 365 366 protected void reset() 367 { 368 super.reset(); 369 370 for (int i = 0; i < variables.length; i++) 371 { 372 variables[i].setValue(null); 373 variables[i].setInitialized(true); 374 } 375 } 376 377 protected void evaluate(byte evaluation) throws JRException 378 { 379 reset(); 380 381 evaluatePrintWhenExpression(evaluation); 382 383 if (isPrintWhenExpressionNull() || isPrintWhenTrue()) 384 { 385 dataset.evaluateDatasetRun(evaluation); 386 387 initEvaluator(evaluation); 388 389 bucketingService.processData(); 390 391 hasData = bucketingService.hasData(); 392 393 if (hasData) 394 { 395 columnHeadersData = bucketingService.getColumnHeaders(); 396 rowHeadersData = bucketingService.getRowHeaders(); 397 cellData = bucketingService.getCrosstabCells(); 398 if (percentage) 399 { 400 grandTotals = bucketingService.getGrandTotals(); 401 } 402 403 crosstabFiller.initCrosstab(); 404 } 405 } 406 } 407 408 protected void initEvaluator(byte evaluation) throws JRException 409 { 410 Map parameterValues = 411 JRFillSubreport.getParameterValues( 412 filler, 413 getParametersMapExpression(), 414 getParameters(), 415 evaluation, 416 true, 417 false, false ); 420 421 ResourceBundle resBdl = (ResourceBundle ) parameterValues.get(JRParameter.REPORT_RESOURCE_BUNDLE); 422 if (resBdl == null) 423 { 424 JRFillParameter resourceBundleParam = (JRFillParameter) filler.getParametersMap().get(JRParameter.REPORT_RESOURCE_BUNDLE); 425 parameterValues.put(JRParameter.REPORT_RESOURCE_BUNDLE, resourceBundleParam.getValue()); 426 } 427 428 parameterValues.put(JRParameter.REPORT_PARAMETERS_MAP, parameterValues); 429 430 for (int i = 0; i < parameters.length; i++) 431 { 432 Object value = parameterValues.get(parameters[i].getName()); 433 parameters[i].setValue(value); 434 } 435 436 crosstabEvaluator.init(parametersMap, variablesMap, filler.getWhenResourceMissingType()); 437 } 438 439 protected void initBucketingService() 440 { 441 if (bucketingService == null) 442 { 443 try 444 { 445 bucketingService = createService(JRExpression.EVALUATION_TIME_NOW); 446 } 447 catch (JRException e) 448 { 449 throw new JRRuntimeException("Could not create bucketing service", e); 450 } 451 } 452 else 453 { 454 bucketingService.clear(); 455 } 456 } 457 458 protected boolean prepare(int availableStretchHeight, boolean isOverflow) throws JRException 459 { 460 super.prepare(availableStretchHeight, isOverflow); 461 462 if (!isToPrint()) 463 { 464 return false; 465 } 466 467 if (availableStretchHeight < getRelativeY() - getY() - getBandBottomY()) 468 { 469 setToPrint(false); 470 return true; 471 } 472 473 if (isOverflow && crosstabFiller.ended() && isAlreadyPrinted()) 474 { 475 if (isPrintWhenDetailOverflows()) 476 { 477 rewind(); 478 setReprinted(true); 479 } 480 else 481 { 482 setStretchHeight(getHeight()); 483 setToPrint(false); 484 485 return false; 486 } 487 } 488 489 if (isOverflow && isPrintWhenDetailOverflows()) 490 { 491 setReprinted(true); 492 } 493 494 int availableHeight = getHeight() + availableStretchHeight - getRelativeY() + getY() + getBandBottomY(); 495 crosstabFiller.fill(availableHeight); 496 497 boolean willOverflow = crosstabFiller.willOverflow(); 498 setStretchHeight(willOverflow ? availableHeight : crosstabFiller.getUsedHeight()); 499 500 return willOverflow; 501 } 502 503 protected JRPrintElement fill() 504 { 505 JRPrintRectangle printRectangle = null; 506 507 printRectangle = new JRTemplatePrintRectangle(getJRTemplateRectangle()); 508 printRectangle.setX(getX()); 509 printRectangle.setY(getRelativeY()); 510 printRectangle.setWidth(getWidth()); 511 printRectangle.setHeight(getStretchHeight()); 512 513 return printRectangle; 514 } 515 516 protected JRTemplateRectangle getJRTemplateRectangle() 517 { 518 JRStyle style = getStyle(); 519 JRTemplateRectangle template = (JRTemplateRectangle) getTemplate(style); 520 if (template == null) 521 { 522 JRDesignRectangle rectangle = new JRDesignRectangle(); 523 524 rectangle.setKey(getKey()); 525 rectangle.setPositionType(getPositionType()); 526 rectangle.setMode(getMode()); 528 rectangle.setX(getX()); 529 rectangle.setY(getY()); 530 rectangle.setWidth(getWidth()); 531 rectangle.setHeight(getHeight()); 532 rectangle.setRemoveLineWhenBlank(isRemoveLineWhenBlank()); 533 rectangle.setPrintInFirstWholeBand(isPrintInFirstWholeBand()); 534 rectangle.setPrintWhenDetailOverflows(isPrintWhenDetailOverflows()); 535 rectangle.setPrintWhenGroupChanges(getPrintWhenGroupChanges()); 536 rectangle.setForecolor(getForecolor()); 537 rectangle.setBackcolor(getBackcolor()); 538 rectangle.setPen(JRGraphicElement.PEN_NONE); 539 540 template = new JRTemplateRectangle(filler.getJasperPrint().getDefaultStyleProvider(), rectangle); 541 542 registerTemplate(style, template); 543 } 544 545 return template; 546 } 547 548 protected void rewind() 549 { 550 crosstabFiller.initCrosstab(); 551 } 552 553 protected List getPrintElements() 554 { 555 return crosstabFiller.getPrintElements(); 556 } 557 558 protected void resolveElement(JRPrintElement element, byte evaluation) 559 { 560 } 562 563 public void collectExpressions(JRExpressionCollector collector) 564 { 565 collector.collect(this); 566 } 567 568 public JRChild getCopy(JRAbstractObjectFactory factory) 569 { 570 return factory.getCrosstab(this); 571 } 572 573 public void writeXml(JRXmlWriter writer) throws IOException 574 { 575 writer.writeCrosstab(this); 576 } 577 578 public int getId() 579 { 580 return parentCrosstab.getId(); 581 } 582 583 public JRCrosstabDataset getDataset() 584 { 585 return dataset; 586 } 587 588 public JRCrosstabRowGroup[] getRowGroups() 589 { 590 return rowGroups; 591 } 592 593 public JRCrosstabColumnGroup[] getColumnGroups() 594 { 595 return columnGroups; 596 } 597 598 public JRCrosstabMeasure[] getMeasures() 599 { 600 return measures; 601 } 602 603 604 609 public class JRFillCrosstabDataset extends JRFillElementDataset implements JRCrosstabDataset 610 { 611 private Object [] bucketValues; 612 613 private Object [] measureValues; 614 615 public JRFillCrosstabDataset(JRCrosstabDataset dataset, JRFillObjectFactory factory) 616 { 617 super(dataset, factory); 618 619 this.bucketValues = new Object [rowGroups.length + columnGroups.length]; 620 this.measureValues = new Object [measures.length]; 621 } 622 623 protected void customInitialize() 624 { 625 initBucketingService(); 626 } 627 628 protected void customEvaluate(JRCalculator calculator) throws JRExpressionEvalException 629 { 630 for (int i = 0; i < rowGroups.length; i++) 631 { 632 bucketValues[i] = calculator.evaluate(rowGroups[i].getBucket().getExpression()); 633 } 634 635 for (int i = 0; i < columnGroups.length; ++i) 636 { 637 bucketValues[i + rowGroups.length] = calculator.evaluate(columnGroups[i].getBucket().getExpression()); 638 } 639 640 for (int i = 0; i < measures.length; i++) 641 { 642 measureValues[i] = calculator.evaluate(measures[i].getValueExpression()); 643 } 644 } 645 646 protected void customIncrement() 647 { 648 try 649 { 650 bucketingService.addData(bucketValues, measureValues); 651 } 652 catch (JRException e) 653 { 654 throw new JRRuntimeException("Error incrementing crosstab dataset", e); 655 } 656 } 657 658 protected Dataset getCustomDataset() 659 { 660 return null; 661 } 662 663 public void collectExpressions(JRExpressionCollector collector) 664 { 665 } 666 667 public boolean isDataPreSorted() 668 { 669 return ((JRCrosstabDataset) parent).isDataPreSorted(); 670 } 671 } 672 673 678 protected class CrosstabFiller 679 { 680 private int yOffset; 681 private boolean willOverflow; 682 683 private int[] rowHeadersXOffsets; 684 685 private boolean[] columnBreakable; 686 private boolean[] rowBreakable; 687 private int[] columnCount; 688 private int[] rowCount; 689 private int[] columnXOffsets; 690 691 private boolean noDataCellPrinted; 692 693 private int startRowIndex; 694 private int startColumnIndex; 695 private int lastColumnIndex; 696 private List columnHeaders; 697 698 private List printRows; 699 700 private HeaderCell[] spanHeaders; 701 private int[] spanHeadersStart; 702 703 private List rowYs = new ArrayList (); 704 private int rowIdx; 705 706 private List preparedRow = new ArrayList (); 707 private int preparedRowHeight; 708 709 private boolean printRowHeaders; 710 private boolean printColumnHeaders; 711 712 private JRFillVariable rowCountVar; 713 private JRFillVariable colCountVar; 714 715 protected CrosstabFiller() 716 { 717 setRowHeadersXOffsets(); 718 719 printRows = new ArrayList (); 720 721 rowCountVar = (JRFillVariable) variablesMap.get(JRCrosstab.VARIABLE_ROW_COUNT); 722 colCountVar = (JRFillVariable) variablesMap.get(JRCrosstab.VARIABLE_COLUMN_COUNT); 723 } 724 725 protected void initCrosstab() 726 { 727 columnXOffsets = computeOffsets(columnHeadersData, columnGroups, true); 728 columnBreakable = computeBreakableHeaders(columnHeadersData, columnGroups, columnXOffsets, true, true); 729 columnCount = computeCounts(columnHeadersData); 730 731 int[] rowYOffsets = computeOffsets(rowHeadersData, rowGroups, false); 732 rowBreakable = computeBreakableHeaders(rowHeadersData, rowGroups, rowYOffsets, false, false); 733 rowCount = computeCounts(rowHeadersData); 734 735 spanHeaders = new HeaderCell[rowGroups.length - 1]; 736 spanHeadersStart = new int[rowGroups.length - 1]; 737 738 startRowIndex = 0; 739 startColumnIndex = 0; 740 lastColumnIndex = 0; 741 noDataCellPrinted = false; 742 } 743 744 protected void setRowHeadersXOffsets() 745 { 746 rowHeadersXOffsets = new int[rowGroups.length + 1]; 747 rowHeadersXOffsets[0] = 0; 748 for (int i = 0; i < rowGroups.length; i++) 749 { 750 rowHeadersXOffsets[i + 1] = rowHeadersXOffsets[i] + rowGroups[i].getWidth(); 751 } 752 } 753 754 protected int[] computeOffsets(HeaderCell[][] headersData, JRFillCrosstabGroup[] groups, boolean width) 755 { 756 int[] offsets = new int[headersData[0].length + 1]; 757 offsets[0] = 0; 758 for (int i = 0; i < headersData[0].length; i++) 759 { 760 int size = 0; 761 for (int j = groups.length - 1; j >= 0; --j) 762 { 763 if (headersData[j][i] != null) 764 { 765 JRFillCellContents cell = headersData[j][i].isTotal() ? groups[j].getFillTotalHeader() : groups[j].getFillHeader(); 766 size = cell == null ? 0 : (width ? cell.getWidth() : cell.getHeight()); 767 break; 768 } 769 } 770 771 offsets[i + 1] = offsets[i] + size; 772 } 773 774 return offsets; 775 } 776 777 protected boolean[] computeBreakableHeaders(HeaderCell[][] headersData, JRFillCrosstabGroup[] groups, int[] offsets, boolean width, boolean startHeaders) 778 { 779 boolean[] breakable = new boolean[headersData[0].length]; 780 for (int i = 0; i < breakable.length; i++) 781 { 782 breakable[i] = true; 783 } 784 785 for (int j = 0; j < groups.length; ++j) 786 { 787 JRFillCellContents fillHeader = groups[j].getFillHeader(); 788 789 if (fillHeader != null) 790 { 791 int size = width ? fillHeader.getWidth() : fillHeader.getHeight(); 792 793 for (int i = 0; i < headersData[0].length; i++) 794 { 795 HeaderCell header = headersData[j][i]; 796 if (header != null && !header.isTotal() && header.getLevelSpan() > 1) 797 { 798 int span = header.getLevelSpan(); 799 800 if (startHeaders) 801 { 802 for (int k = i + 1; k < i + span && offsets[k] - offsets[i] < size; ++k) 803 { 804 breakable[k] = false; 805 } 806 } 807 808 for (int k = i + span - 1; k > i && offsets[i + span] - offsets[k] < size; --k) 809 { 810 breakable[k] = false; 811 } 812 } 813 } 814 } 815 } 816 817 return breakable; 818 } 819 820 private int[] computeCounts(HeaderCell[][] headersData) 821 { 822 int[] counts = new int[headersData[0].length]; 823 824 HeaderCell[] lastHeaders = headersData[headersData.length - 1]; 825 for (int i = 0, c = 0; i < counts.length; ++i) 826 { 827 HeaderCell lastHeader = lastHeaders[i]; 828 if (lastHeader != null && !lastHeader.isTotal()) 829 { 830 ++c; 831 } 832 833 counts[i] = c; 834 } 835 836 return counts; 837 } 838 839 protected void fill(int availableHeight) throws JRException 840 { 841 printRows.clear(); 842 843 yOffset = 0; 844 willOverflow = false; 845 846 fillVerticalCrosstab(availableHeight); 847 } 848 849 protected boolean willOverflow() 850 { 851 return willOverflow; 852 } 853 854 protected int getUsedHeight() 855 { 856 return yOffset; 857 } 858 859 protected boolean ended() 860 { 861 return hasData ? (startRowIndex >= rowHeadersData[0].length && startColumnIndex >= columnHeadersData[0].length) : noDataCellPrinted; 862 } 863 864 protected void fillVerticalCrosstab(int availableHeight) throws JRException 865 { 866 if (!hasData) 867 { 868 fillNoDataCell(availableHeight); 869 return; 870 } 871 872 printRowHeaders = startColumnIndex == 0 || isRepeatRowHeaders(); 873 int rowHeadersXOffset = printRowHeaders ? rowHeadersXOffsets[rowGroups.length] : 0; 874 875 if (startColumnIndex == lastColumnIndex) 876 { 877 int availableWidth = getWidth(); 878 879 columnHeaders = getGroupHeaders(availableWidth - rowHeadersXOffset, columnXOffsets, columnBreakable, startColumnIndex, columnHeadersData, columnGroups); 880 lastColumnIndex = startColumnIndex + columnHeaders.size(); 881 882 if (startColumnIndex == lastColumnIndex) 883 { 884 throw new JRRuntimeException("Not enough space to render the crosstab."); 885 } 886 } 887 888 printColumnHeaders = startRowIndex == 0 || isRepeatColumnHeaders(); 889 List columnHeaderRows = null; 890 if (printColumnHeaders) 891 { 892 columnHeaderRows = fillColumnHeaders(rowHeadersXOffset, availableHeight - yOffset); 893 if (willOverflow) 894 { 895 return; 896 } 897 } 898 899 int lastRowIndex = fillRows(rowHeadersXOffset, availableHeight - yOffset); 900 901 if (lastRowIndex == startRowIndex) 902 { 903 willOverflow = true; 904 return; 905 } 906 907 if (columnHeaderRows != null) 908 { 909 printRows.addAll(columnHeaderRows); 910 } 911 912 if (lastRowIndex >= rowHeadersData[0].length) 913 { 914 startColumnIndex = lastColumnIndex; 915 916 if (startColumnIndex < columnHeadersData[0].length) 917 { 918 startRowIndex = lastRowIndex = 0; 919 920 yOffset += getColumnBreakOffset(); 921 fillVerticalCrosstab(availableHeight); 922 return; 923 } 924 } 925 926 boolean fillEnded = lastRowIndex >= rowHeadersData[0].length && lastColumnIndex >= columnHeadersData[0].length; 927 if (fillEnded) 928 { 929 setStretchHeight(yOffset); 930 } 931 else 932 { 933 setStretchHeight(availableHeight); 934 } 935 936 startRowIndex = lastRowIndex; 937 938 willOverflow = !fillEnded; 939 } 940 941 942 protected List getGroupHeaders(int available, int[] offsets, boolean[] breakable, int firstIndex, HeaderCell[][] headersData, JRFillCrosstabGroup[] groups) 943 { 944 List headers = new ArrayList (); 945 946 int maxOffset = available + offsets[firstIndex]; 947 int lastIndex; 948 for (lastIndex = firstIndex; lastIndex < headersData[0].length && offsets[lastIndex + 1] <= maxOffset; ++lastIndex) 949 { 950 HeaderCell[] groupHeaders = new HeaderCell[groups.length]; 951 952 for (int j = 0; j < groups.length; ++j) 953 { 954 groupHeaders[j] = headersData[j][lastIndex]; 955 } 956 957 headers.add(groupHeaders); 958 } 959 960 961 if (lastIndex < headersData[0].length) 962 { 963 while(lastIndex > firstIndex && !breakable[lastIndex]) 964 { 965 --lastIndex; 966 headers.remove(headers.size() - 1); 967 } 968 } 969 970 if (lastIndex > firstIndex) 971 { 972 if (firstIndex > 0) 973 { 974 HeaderCell[] firstHeaders = (HeaderCell[]) headers.get(0); 975 976 for (int j = 0; j < groups.length; ++j) 977 { 978 HeaderCell header = headersData[j][firstIndex]; 979 980 if (header == null) 981 { 982 int spanIndex = getSpanIndex(firstIndex, j, headersData); 983 if (spanIndex >= 0) 984 { 985 HeaderCell spanCell = headersData[j][spanIndex]; 986 int headerEndIdx = spanCell.getLevelSpan() + spanIndex; 987 if (headerEndIdx > lastIndex) 988 { 989 headerEndIdx = lastIndex; 990 } 991 firstHeaders[j] = HeaderCell.createLevelSpanCopy(spanCell, headerEndIdx - firstIndex); 992 } 993 } 994 } 995 } 996 997 if (lastIndex < headersData[0].length) 998 { 999 for (int j = 0; j < groups.length; ++j) 1000 { 1001 HeaderCell header = headersData[j][lastIndex]; 1002 1003 if (header == null) 1004 { 1005 int spanIndex = getSpanIndex(lastIndex, j, headersData); 1006 if (spanIndex >= firstIndex) 1007 { 1008 HeaderCell spanCell = headersData[j][spanIndex]; 1009 HeaderCell[] headerCells = (HeaderCell[]) headers.get(spanIndex - firstIndex); 1010 headerCells[j] = HeaderCell.createLevelSpanCopy(spanCell, lastIndex - spanIndex); 1011 } 1012 } 1013 } 1014 } 1015 } 1016 1017 return headers; 1018 } 1019 1020 1021 protected int getSpanIndex(int i, int j, HeaderCell[][] headersData) 1022 { 1023 int spanIndex = i - 1; 1024 while (spanIndex >= 0 && headersData[j][spanIndex] == null) 1025 { 1026 --spanIndex; 1027 } 1028 1029 if (spanIndex >= 0) 1030 { 1031 HeaderCell spanCell = headersData[j][spanIndex]; 1032 int span = spanCell.getLevelSpan(); 1033 1034 if (span > i - spanIndex) 1035 { 1036 return spanIndex; 1037 } 1038 } 1039 1040 return -1; 1041 } 1042 1043 1044 protected void fillNoDataCell(int availableHeight) throws JRException 1045 { 1046 if (whenNoDataCell == null) 1047 { 1048 noDataCellPrinted = true; 1049 } 1050 else 1051 { 1052 if (availableHeight < whenNoDataCell.getHeight()) 1053 { 1054 willOverflow = true; 1055 } 1056 else 1057 { 1058 whenNoDataCell.evaluate(JRExpression.EVALUATION_DEFAULT); 1059 whenNoDataCell.prepare(availableHeight - whenNoDataCell.getHeight()); 1060 1061 willOverflow = whenNoDataCell.willOverflow(); 1062 1063 if (!willOverflow) 1064 { 1065 whenNoDataCell.setX(0); 1066 whenNoDataCell.setY(0); 1067 1068 JRPrintFrame printCell = whenNoDataCell.fill(); 1069 List noDataRow = new ArrayList (1); 1070 noDataRow.add(printCell); 1071 addPrintRow(noDataRow); 1072 1073 yOffset += whenNoDataCell.getPrintHeight(); 1074 noDataCellPrinted = true; 1075 } 1076 } 1077 } 1078 } 1079 1080 1081 protected List fillColumnHeaders(int rowHeadersXOffset, int availableHeight) throws JRException 1082 { 1083 JRFillCellContents[][] columnHeaderRows = new JRFillCellContents[columnGroups.length][lastColumnIndex - startColumnIndex + 1]; 1084 1085 rowYs.clear(); 1086 rowYs.add(new Integer (0)); 1087 1088 if (printRowHeaders && headerCell != null) 1089 { 1090 JRFillCellContents contents = fillHeader(availableHeight); 1091 1092 if (willOverflow) 1093 { 1094 return null; 1095 } 1096 1097 columnHeaderRows[columnGroups.length - 1][0] = contents; 1098 } 1099 1100 rows: 1101 for (rowIdx = 0; rowIdx < columnGroups.length; rowIdx++) 1102 { 1103 for (int columnIdx = startColumnIndex; columnIdx < lastColumnIndex; ++columnIdx) 1104 { 1105 HeaderCell[] headers = (HeaderCell[]) columnHeaders.get(columnIdx - startColumnIndex); 1106 HeaderCell cell = headers[rowIdx]; 1107 1108 if (cell != null) 1109 { 1110 JRFillCellContents contents = prepareColumnHeader(cell, columnIdx, rowHeadersXOffset, availableHeight); 1111 columnHeaderRows[rowIdx + cell.getDepthSpan() - 1][columnIdx - startColumnIndex + 1] = contents; 1112 1113 if (willOverflow) 1114 { 1115 break rows; 1116 } 1117 } 1118 } 1119 1120 int rowStretchHeight = stretchColumnHeadersRow(columnHeaderRows[rowIdx]); 1121 rowYs.add(new Integer (((Integer ) rowYs.get(rowIdx)).intValue() + rowStretchHeight)); 1122 } 1123 1124 List headerRows; 1125 if (willOverflow) 1126 { 1127 headerRows = null; 1128 releaseColumnHeaderCells(columnHeaderRows); 1129 } 1130 else 1131 { 1132 headerRows = fillColumnHeaders(columnHeaderRows); 1133 yOffset += ((Integer ) rowYs.get(columnGroups.length)).intValue(); 1134 } 1135 1136 resetVariables(); 1137 1138 return headerRows; 1139 } 1140 1141 1142 private void setCountVars(int rowIdx, int colIdx) 1143 { 1144 if (rowIdx == -1) 1145 { 1146 rowCountVar.setValue(null); 1147 } 1148 else 1149 { 1150 rowCountVar.setValue(new Integer (rowCount[rowIdx])); 1151 } 1152 1153 if (colIdx == -1) 1154 { 1155 colCountVar.setValue(null); 1156 } 1157 else 1158 { 1159 colCountVar.setValue(new Integer (columnCount[colIdx])); 1160 } 1161 } 1162 1163 private JRFillCellContents fillHeader(int availableHeight) throws JRException 1164 { 1165 setCountVars(-1, -1); 1166 1167 JRFillCellContents contents = headerCell.getWorkingClone(); 1168 contents.evaluate(JRExpression.EVALUATION_DEFAULT); 1169 contents.prepare(availableHeight - headerCell.getHeight()); 1170 1171 willOverflow = contents.willOverflow(); 1172 1173 if (!willOverflow) 1174 { 1175 contents.setX(0); 1176 contents.setY(yOffset); 1177 contents.setVerticalSpan(columnGroups.length); 1178 } 1179 return contents; 1180 } 1181 1182 private JRFillCellContents prepareColumnHeader(HeaderCell cell, int columnIdx, int xOffset, int availableHeight) throws JRException 1183 { 1184 JRFillCrosstabColumnGroup group = columnGroups[rowIdx]; 1185 JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader(); 1186 1187 int width = columnXOffsets[columnIdx + cell.getLevelSpan()] - columnXOffsets[columnIdx]; 1188 int height = contents.getHeight(); 1189 1190 if (width <= 0 || height <= 0) 1191 { 1192 return null; 1193 } 1194 1195 JRFillCellContents preparedContents = null; 1196 1197 int rowY = ((Integer ) rowYs.get(rowIdx)).intValue(); 1198 int cellAvailableStretch = availableHeight - rowY - height; 1199 1200 if (cellAvailableStretch >= 0) 1201 { 1202 setCountVars(-1, columnIdx); 1203 setGroupVariables(columnGroups, cell.getBucketValues()); 1204 1205 contents = contents.getTransformedContents(width, height, group.getPosition(), JRCellContents.POSITION_Y_TOP); 1206 contents = contents.getBoxContents(columnIdx == startColumnIndex && (!printRowHeaders || headerCell == null), false); 1207 contents = contents.getWorkingClone(); 1208 1209 contents.evaluate(JRExpression.EVALUATION_DEFAULT); 1210 contents.prepare(cellAvailableStretch); 1211 1212 if (contents.willOverflow()) 1213 { 1214 willOverflow = true; 1215 } 1216 else 1217 { 1218 contents.setX(columnXOffsets[columnIdx] - columnXOffsets[startColumnIndex] + xOffset); 1219 contents.setY(rowY + yOffset); 1220 contents.setVerticalSpan(cell.getDepthSpan()); 1221 1222 preparedContents = contents; 1223 } 1224 } 1225 else 1226 { 1227 willOverflow = true; 1228 } 1229 1230 return preparedContents; 1231 } 1232 1233 1234 private int stretchColumnHeadersRow(JRFillCellContents[] headers) 1235 { 1236 int rowY = ((Integer ) rowYs.get(rowIdx)).intValue(); 1237 1238 int rowStretchHeight = 0; 1239 for (int j = 0; j < headers.length; j++) 1240 { 1241 JRFillCellContents contents = headers[j]; 1242 1243 if (contents != null) 1244 { 1245 int startRowY = rowY; 1246 if (contents.getVerticalSpan() > 1) 1247 { 1248 startRowY = ((Integer ) rowYs.get(rowIdx - contents.getVerticalSpan() + 1)).intValue(); 1249 } 1250 1251 int height = contents.getPrintHeight() - rowY + startRowY; 1252 1253 if (height > rowStretchHeight) 1254 { 1255 rowStretchHeight = height; 1256 } 1257 } 1258 } 1259 1260 for (int j = 0; j < headers.length; j++) 1261 { 1262 JRFillCellContents contents = headers[j]; 1263 1264 if (contents != null) 1265 { 1266 int startRowY = rowY; 1267 if (contents.getVerticalSpan() > 1) 1268 { 1269 startRowY = ((Integer ) rowYs.get(rowIdx - contents.getVerticalSpan() + 1)).intValue(); 1270 } 1271 1272 contents.stretchTo(rowStretchHeight + rowY - startRowY); 1273 } 1274 } 1275 1276 return rowStretchHeight; 1277 } 1278 1279 1280 private List fillColumnHeaders(JRFillCellContents[][] columnHeaderRows) throws JRException 1281 { 1282 List headerRows = new ArrayList (columnGroups.length); 1283 1284 for (int i = 0; i < columnHeaderRows.length; ++i) 1285 { 1286 List headerRow = new ArrayList (lastColumnIndex - startColumnIndex); 1287 headerRows.add(headerRow); 1288 1289 for (int j = 0; j < columnHeaderRows[i].length; j++) 1290 { 1291 JRFillCellContents contents = columnHeaderRows[i][j]; 1292 1293 if (contents != null) 1294 { 1295 headerRow.add(contents.fill()); 1296 contents.releaseWorkingClone(); 1297 } 1298 } 1299 } 1300 1301 return headerRows; 1302 } 1303 1304 private void releaseColumnHeaderCells(JRFillCellContents[][] columnHeaderRows) throws JRException 1305 { 1306 for (int i = 0; i < columnHeaderRows.length; ++i) 1307 { 1308 for (int j = 0; j < columnHeaderRows[i].length; j++) 1309 { 1310 JRFillCellContents contents = columnHeaderRows[i][j]; 1311 1312 if (contents != null) 1313 { 1314 contents.rewind(); 1315 contents.releaseWorkingClone(); 1316 } 1317 } 1318 } 1319 } 1320 1321 protected int fillRows(int xOffset, int availableHeight) throws JRException 1322 { 1323 rowYs.clear(); 1324 rowYs.add(new Integer (0)); 1325 1326 for (rowIdx = 0; rowIdx < cellData.length - startRowIndex; ++rowIdx) 1327 { 1328 initPreparedRow(); 1329 1330 prepareRow(xOffset, availableHeight); 1331 1332 if (willOverflow) 1333 { 1334 break; 1335 } 1336 1337 fillRow(); 1338 1339 rowYs.add(new Integer (((Integer ) rowYs.get(rowIdx)).intValue() + preparedRowHeight)); 1340 } 1341 1342 if (rowIdx < cellData.length - startRowIndex) { 1344 releasePreparedRow(); 1345 1346 if (printRowHeaders) 1347 { 1348 fillContinuingRowHeaders(xOffset, availableHeight); 1349 } 1350 } 1351 1352 yOffset += ((Integer ) rowYs.get(rowIdx)).intValue(); 1353 1354 return rowIdx + startRowIndex; 1355 } 1356 1357 private void initPreparedRow() 1358 { 1359 preparedRow.clear(); 1360 preparedRowHeight = 0; 1361 } 1362 1363 private void removeFilledRows(int rowsToRemove) 1364 { 1365 if (rowsToRemove > 0) 1366 { 1367 for (int i = 0; i < rowsToRemove; ++i) 1368 { 1369 printRows.remove(printRows.size() - 1); 1370 rowYs.remove(rowYs.size() - 1); 1371 } 1372 1373 rowIdx -= rowsToRemove; 1374 } 1375 } 1376 1377 private void releasePreparedRow() throws JRException 1378 { 1379 for (Iterator it = preparedRow.iterator(); it.hasNext();) 1380 { 1381 JRFillCellContents cell = (JRFillCellContents) it.next(); 1382 cell.rewind(); 1383 cell.releaseWorkingClone(); 1384 } 1385 1386 preparedRow.clear(); 1387 } 1388 1389 private void fillRow() throws JRException 1390 { 1391 int rowY = ((Integer ) rowYs.get(rowIdx)).intValue(); 1392 1393 List rowPrints = new ArrayList (preparedRow.size()); 1394 for (Iterator it = preparedRow.iterator(); it.hasNext();) 1395 { 1396 JRFillCellContents cell = (JRFillCellContents) it.next(); 1397 1398 int spanHeight = 0; 1399 if (cell.getVerticalSpan() > 1) 1400 { 1401 spanHeight = rowY - ((Integer ) rowYs.get(rowIdx - cell.getVerticalSpan() + 1)).intValue(); 1402 } 1403 1404 cell.stretchTo(preparedRowHeight + spanHeight); 1405 rowPrints.add(cell.fill()); 1406 1407 cell.releaseWorkingClone(); 1408 } 1409 1410 addPrintRow(rowPrints); 1411 } 1412 1413 private void prepareRow(int xOffset, int availableHeight) throws JRException 1414 { 1415 for (int col = startColumnIndex; col < lastColumnIndex; ++col) 1416 { 1417 CrosstabCell data = cellData[rowIdx + startRowIndex][col]; 1418 boolean overflow = prepareDataCell(data, col, availableHeight, xOffset); 1419 1420 if (overflow) 1421 { 1422 willOverflow = true; 1423 return; 1424 } 1425 } 1426 1427 resetVariables(); 1428 1429 if (printRowHeaders) 1430 { 1431 for (int j = 0; j < rowGroups.length; j++) 1432 { 1433 HeaderCell cell = rowHeadersData[j][rowIdx + startRowIndex]; 1434 1435 boolean overflow = false; 1436 if (cell == null) 1437 { 1438 overflow = prepareClosingRowHeader(j, availableHeight); 1439 } 1440 else 1441 { 1442 if (cell.getLevelSpan() > 1) 1443 { 1444 spanHeaders[j] = cell; 1445 spanHeadersStart[j] = rowIdx + startRowIndex; 1446 continue; 1447 } 1448 1449 overflow = prepareRowHeader(j, cell, 1, availableHeight); 1450 } 1451 1452 if (overflow) 1453 { 1454 willOverflow = true; 1455 return; 1456 } 1457 } 1458 1459 resetVariables(); 1460 } 1461 } 1462 1463 private boolean prepareDataCell(CrosstabCell data, int column, int availableHeight, int xOffset) throws JRException 1464 { 1465 int rowY = ((Integer ) rowYs.get(rowIdx)).intValue(); 1466 1467 JRFillCrosstabCell cell = crossCells[data.getRowTotalGroupIndex()][data.getColumnTotalGroupIndex()]; 1468 JRFillCellContents contents = cell == null ? null : cell.getFillContents(); 1469 if (contents == null || contents.getWidth() <= 0 || contents.getHeight() <= 0) 1470 { 1471 return false; 1472 } 1473 1474 int cellAvailableStretch = availableHeight - rowY - contents.getHeight(); 1475 boolean overflow = cellAvailableStretch < 0; 1476 if (!overflow) 1477 { 1478 boolean leftEmpty = startColumnIndex != 0 && !isRepeatRowHeaders(); 1479 boolean topEmpty = startRowIndex != 0 && !isRepeatColumnHeaders(); 1480 1481 setCountVars(rowIdx + startRowIndex, column); 1482 setGroupVariables(rowGroups, data.getRowBucketValues()); 1483 setGroupVariables(columnGroups, data.getColumnBucketValues()); 1484 setMeasureVariables(data); 1485 1486 contents = contents.getBoxContents(leftEmpty && column == startColumnIndex, topEmpty && rowIdx == 0); 1487 contents = contents.getWorkingClone(); 1488 1489 contents.evaluate(JRExpression.EVALUATION_DEFAULT); 1490 contents.prepare(cellAvailableStretch); 1491 1492 preparedRow.add(contents); 1493 1494 overflow = contents.willOverflow(); 1495 1496 if (!overflow) 1497 { 1498 contents.setX(columnXOffsets[column] - columnXOffsets[startColumnIndex] + xOffset); 1499 contents.setY(rowY + yOffset); 1500 1501 int rowCellHeight = contents.getPrintHeight(); 1502 if (rowCellHeight > preparedRowHeight) 1503 { 1504 preparedRowHeight = rowCellHeight; 1505 } 1506 } 1507 } 1508 1509 return overflow; 1510 } 1511 1512 private boolean prepareRowHeader(int rowGroup, HeaderCell cell, int vSpan, int availableHeight) throws JRException 1513 { 1514 JRFillCrosstabRowGroup group = rowGroups[rowGroup]; 1515 JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader(); 1516 1517 if (contents.getWidth() <= 0 || contents.getHeight() <= 0) 1518 { 1519 return false; 1520 } 1521 1522 int spanHeight = 0; 1523 int headerY = ((Integer ) rowYs.get(rowIdx - vSpan + 1)).intValue(); 1524 if (vSpan > 1) 1525 { 1526 spanHeight += ((Integer ) rowYs.get(rowIdx)).intValue() - headerY; 1527 } 1528 int rowHeight = spanHeight + preparedRowHeight; 1529 1530 boolean stretchContents = group.getPosition() == JRCellContents.POSITION_Y_STRETCH; 1531 int contentsHeight = stretchContents ? rowHeight : contents.getHeight(); 1532 1533 int cellAvailableStretch = availableHeight - headerY - contentsHeight; 1534 boolean headerOverflow = cellAvailableStretch < 0 || rowHeight < contents.getHeight(); 1535 1536 if (!headerOverflow) 1537 { 1538 setCountVars(rowIdx + startRowIndex - vSpan + 1, -1); 1539 setGroupVariables(rowGroups, cell.getBucketValues()); 1540 1541 if (stretchContents) 1542 { 1543 contents = contents.getTransformedContents(contents.getWidth(), rowHeight, JRCellContents.POSITION_X_LEFT, JRCellContents.POSITION_Y_STRETCH); 1544 } 1545 contents = contents.getBoxContents(false, rowIdx + 1 == vSpan && (!printColumnHeaders || headerCell == null)); 1546 contents.getWorkingClone(); 1547 1548 contents.evaluate(JRExpression.EVALUATION_DEFAULT); 1549 contents.prepare(cellAvailableStretch); 1550 1551 preparedRow.add(contents); 1552 1553 headerOverflow = contents.willOverflow(); 1554 1555 if (!headerOverflow) 1556 { 1557 contents.setX(rowHeadersXOffsets[rowGroup]); 1558 contents.setY(headerY + yOffset); 1559 contents.setVerticalSpan(vSpan); 1560 1561 int rowCellHeight = contents.getPrintHeight() - spanHeight; 1562 if (rowCellHeight > preparedRowHeight) 1563 { 1564 preparedRowHeight = rowCellHeight; 1565 } 1566 } 1567 } 1568 1569 if (headerOverflow) 1570 { 1571 removeFilledRows(vSpan - 1); 1572 } 1573 1574 return headerOverflow; 1575 } 1576 1577 private boolean prepareClosingRowHeader(int rowGroup, int availableHeight) throws JRException 1578 { 1579 if (rowGroup < rowGroups.length - 1 && 1580 spanHeaders[rowGroup] != null && 1581 spanHeaders[rowGroup].getLevelSpan() + spanHeadersStart[rowGroup] == rowIdx + startRowIndex + 1) 1582 { 1583 HeaderCell cell = spanHeaders[rowGroup]; 1584 int vSpan = cell.getLevelSpan(); 1585 if (spanHeadersStart[rowGroup] < startRowIndex) { 1587 vSpan += spanHeadersStart[rowGroup] - startRowIndex; 1588 } 1589 spanHeaders[rowGroup] = null; 1590 1591 return prepareRowHeader(rowGroup, cell, vSpan, availableHeight); 1592 } 1593 1594 return false; 1595 } 1596 1597 private void removeExceedingSpanHeaders() 1598 { 1599 for (int j = rowGroups.length - 2; j >= 0; --j) 1600 { 1601 if (spanHeaders[j] != null && spanHeadersStart[j] >= rowIdx + startRowIndex) 1602 { 1603 spanHeaders[j] = null; 1604 } 1605 } 1606 } 1607 1608 private void setBackSpanHeaders() 1609 { 1610 for (int j = rowGroups.length - 2; j >= 0 && spanHeaders[j] == null; --j) 1611 { 1612 int spanIndex = getSpanIndex(rowIdx + startRowIndex, j, rowHeadersData); 1613 1614 if (spanIndex >= 0) 1615 { 1616 spanHeaders[j] = rowHeadersData[j][spanIndex]; 1617 spanHeadersStart[j] = spanIndex; 1618 } 1619 } 1620 } 1621 1622 private void fillContinuingRowHeaders(int xOffset, int availableHeight) throws JRException 1623 { 1624 boolean done = false; 1625 breakCrosstab: 1626 do 1627 { 1628 removeExceedingSpanHeaders(); 1629 1630 if (!rowBreakable[rowIdx + startRowIndex]) 1631 { 1632 removeFilledRows(1); 1633 setBackSpanHeaders(); 1634 continue; 1635 } 1636 1637 initPreparedRow(); 1638 1639 for (int j = 0; j < rowGroups.length - 1; ++j) 1641 { 1642 if (spanHeaders[j] != null) 1643 { 1644 boolean headerOverflow = prepareContinuingRowHeader(j, availableHeight); 1645 1646 if (headerOverflow) 1647 { 1648 releasePreparedRow(); 1649 continue breakCrosstab; 1650 } 1651 } 1652 } 1653 1654 if (!preparedRow.isEmpty()) 1655 { 1656 int lastRowHeight = ((Integer ) rowYs.get(rowIdx)).intValue() - ((Integer ) rowYs.get(rowIdx - 1)).intValue(); 1657 1658 if (preparedRowHeight > lastRowHeight) { 1660 refillLastRow(xOffset, availableHeight); 1661 } 1662 else 1663 { 1664 fillContinuingHeaders(lastRowHeight); 1665 } 1666 } 1667 1668 done = true; 1669 } 1670 while (!done && rowIdx > 0); 1671 } 1672 1673 private void fillContinuingHeaders(int lastRowHeight) throws JRException 1674 { 1675 int nextToLastHeaderY = ((Integer ) rowYs.get(rowIdx - 1)).intValue(); 1676 List lastPrintRow = getLastPrintRow(); 1677 1678 for (int j = 0; j < preparedRow.size(); ++j) 1679 { 1680 JRFillCellContents contents = (JRFillCellContents) preparedRow.get(j); 1681 1682 int headerY = ((Integer ) rowYs.get(rowIdx - contents.getVerticalSpan())).intValue(); 1683 1684 contents.stretchTo(nextToLastHeaderY - headerY + lastRowHeight); 1685 lastPrintRow.add(contents.fill()); 1686 contents.releaseWorkingClone(); 1687 } 1688 } 1689 1690 private void refillLastRow(int xOffset, int availableHeight) throws JRException 1691 { 1692 removeFilledRows(1); 1693 setBackSpanHeaders(); 1694 1695 prepareRow(xOffset, availableHeight); 1696 fillRow(); 1697 1698 rowYs.add(new Integer (((Integer ) rowYs.get(rowIdx)).intValue() + preparedRowHeight)); 1699 ++rowIdx; 1700 } 1701 1702 private boolean prepareContinuingRowHeader(int rowGroup, int availableHeight) throws JRException 1703 { 1704 HeaderCell cell = spanHeaders[rowGroup]; 1705 int vSpan = rowIdx + startRowIndex - spanHeadersStart[rowGroup]; 1706 1707 if (spanHeadersStart[rowGroup] < startRowIndex) { 1709 vSpan += spanHeadersStart[rowGroup] - startRowIndex; 1710 } 1711 1712 int headerY = ((Integer ) rowYs.get(rowIdx - vSpan)).intValue(); 1713 int lastHeaderY = ((Integer ) rowYs.get(rowIdx)).intValue(); 1714 int headerHeight = lastHeaderY - headerY; 1715 int nextToLastHeaderY = ((Integer ) rowYs.get(rowIdx - 1)).intValue(); 1716 int stretchHeight = nextToLastHeaderY - headerY; 1717 1718 JRFillCrosstabRowGroup group = rowGroups[rowGroup]; 1719 JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader(); 1720 1721 boolean stretchContents = group.getPosition() == JRCellContents.POSITION_Y_STRETCH; 1722 int contentsHeight = stretchContents ? headerHeight : contents.getHeight(); 1723 1724 int cellAvailableStretch = availableHeight - headerY - contentsHeight; 1725 boolean headerOverflow = cellAvailableStretch < 0 || headerHeight < contents.getHeight(); 1726 if (!headerOverflow) 1727 { 1728 setCountVars(rowIdx + startRowIndex - vSpan, -1); 1729 setGroupVariables(rowGroups, cell.getBucketValues()); 1730 1731 if (stretchContents) 1732 { 1733 contents = contents.getTransformedContents(contents.getWidth(), headerHeight, JRCellContents.POSITION_X_LEFT, JRCellContents.POSITION_Y_STRETCH); 1734 } 1735 1736 contents = contents.getBoxContents(false, rowIdx == vSpan && (!printColumnHeaders || headerCell == null)); 1737 contents.getWorkingClone(); 1738 1739 contents.evaluate(JRExpression.EVALUATION_DEFAULT); 1740 contents.prepare(cellAvailableStretch); 1741 1742 preparedRow.add(contents); 1743 1744 headerOverflow = contents.willOverflow(); 1745 1746 if (!headerOverflow) 1747 { 1748 contents.setX(rowHeadersXOffsets[rowGroup]); 1749 contents.setY(headerY + yOffset); 1750 contents.setVerticalSpan(vSpan); 1751 1752 int rowHeight = contents.getPrintHeight() - stretchHeight; 1753 if (rowHeight > preparedRowHeight) 1754 { 1755 preparedRowHeight = rowHeight; 1756 } 1757 } 1758 } 1759 1760 if (headerOverflow) 1761 { 1762 removeFilledRows(vSpan); 1763 } 1764 1765 return headerOverflow; 1766 } 1767 1768 protected void addPrintRow(List printRow) 1769 { 1770 printRows.add(printRow); 1771 } 1772 1773 protected List getLastPrintRow() 1774 { 1775 return (List ) printRows.get(printRows.size() - 1); 1776 } 1777 1778 protected List getPrintElements() 1779 { 1780 List prints = new ArrayList (); 1781 1782 for (Iterator it = printRows.iterator(); it.hasNext();) 1783 { 1784 List rowPrints = (List ) it.next(); 1785 prints.addAll(rowPrints); 1786 } 1787 1788 return prints; 1789 } 1790 1791 protected void setGroupVariables(JRFillCrosstabGroup[] groups, Bucket[] bucketValues) 1792 { 1793 for (int i = 0; i < groups.length; i++) 1794 { 1795 Object value = null; 1796 if (bucketValues[i] != null && !bucketValues[i].isTotal()) 1797 { 1798 value = bucketValues[i].getValue(); 1799 } 1800 groups[i].getFillVariable().setValue(value); 1801 } 1802 } 1803 1804 protected void setMeasureVariables(CrosstabCell cell) 1805 { 1806 MeasureValue[] values = cell.getMesureValues(); 1807 for (int i = 0; i < measures.length; i++) 1808 { 1809 Object value = measureValue(values, i); 1810 measures[i].getFillVariable().setValue(value); 1811 } 1812 1813 MeasureValue[][][] totals = cell.getTotals(); 1814 for (int row = 0; row <= rowGroups.length; row++) 1815 { 1816 for (int col = 0; col <= columnGroups.length; col++) 1817 { 1818 MeasureValue[] vals = totals[row][col]; 1819 if (retrieveTotal[row][col]) 1820 { 1821 for (int m = 0; m < measures.length; m++) 1822 { 1823 JRFillVariable totalVar = totalVariables[row][col][m]; 1824 Object value = measureValue(vals, m); 1825 totalVar.setValue(value); 1826 } 1827 } 1828 } 1829 } 1830 } 1831 1832 1833 protected Object measureValue(MeasureValue[] values, int measureIdx) 1834 { 1835 Object value; 1836 if (measures[measureIdx].getPercentageOfType() == JRCrosstabMeasure.PERCENTAGE_TYPE_GRAND_TOTAL) 1837 { 1838 if (values[measureIdx].isInitialized()) 1839 { 1840 value = values[measureIdx].getValue(); 1841 } 1842 else 1843 { 1844 value = measures[measureIdx].getPercentageCalculator().calculatePercentage(values[measureIdx], grandTotals[measureIdx]); 1845 } 1846 } 1847 else 1848 { 1849 value = values[measureIdx].getValue(); 1850 } 1851 return value; 1852 } 1853 1854 1855 protected void resetVariables() 1856 { 1857 for (int i = 0; i < rowGroups.length; i++) 1858 { 1859 rowGroups[i].getFillVariable().setValue(null); 1860 } 1861 1862 for (int i = 0; i < columnGroups.length; i++) 1863 { 1864 columnGroups[i].getFillVariable().setValue(null); 1865 } 1866 1867 for (int i = 0; i < measures.length; i++) 1868 { 1869 measures[i].getFillVariable().setValue(null); 1870 } 1871 1872 for (int row = 0; row <= rowGroups.length; ++row) 1873 { 1874 for (int col = 0; col <= columnGroups.length; ++col) 1875 { 1876 if (retrieveTotal[row][col]) 1877 { 1878 for (int i = 0; i < measures.length; i++) 1879 { 1880 totalVariables[row][col][i].setValue(null); 1881 } 1882 } 1883 } 1884 } 1885 } 1886 } 1887 1888 public int getColumnBreakOffset() 1889 { 1890 return parentCrosstab.getColumnBreakOffset(); 1891 } 1892 1893 public boolean isRepeatColumnHeaders() 1894 { 1895 return parentCrosstab.isRepeatColumnHeaders(); 1896 } 1897 1898 public boolean isRepeatRowHeaders() 1899 { 1900 return parentCrosstab.isRepeatRowHeaders(); 1901 } 1902 1903 public JRCrosstabCell[][] getCells() 1904 { 1905 return crossCells; 1906 } 1907 1908 public JRCellContents getWhenNoDataCell() 1909 { 1910 return whenNoDataCell; 1911 } 1912 1913 public JRCrosstabParameter[] getParameters() 1914 { 1915 return parameters; 1916 } 1917 1918 public JRExpression getParametersMapExpression() 1919 { 1920 return parentCrosstab.getParametersMapExpression(); 1921 } 1922 1923 1924 public JRElement getElementByKey(String elementKey) 1925 { 1926 return JRBaseCrosstab.getElementByKey(this, elementKey); 1927 } 1928 1929 public JRCloneable createClone(JRFillCloneFactory factory) 1930 { 1931 return null; 1933 } 1934 1935 public JRCellContents getHeaderCell() 1936 { 1937 return headerCell; 1938 } 1939 1940 public JRVariable[] getVariables() 1941 { 1942 return variables; 1943 } 1944} 1945 | Popular Tags |