1 17 18 19 20 package org.apache.fop.layoutmgr; 21 22 import java.util.LinkedList ; 23 import java.util.List ; 24 import java.util.ListIterator ; 25 26 import org.apache.commons.logging.Log; 27 import org.apache.commons.logging.LogFactory; 28 import org.apache.fop.traits.MinOptMax; 29 30 35 public class SpaceResolver { 36 37 38 protected static Log log = LogFactory.getLog(SpaceResolver.class); 39 40 private UnresolvedListElementWithLength[] firstPart; 41 private BreakElement breakPoss; 42 private UnresolvedListElementWithLength[] secondPart; 43 private UnresolvedListElementWithLength[] noBreak; 44 45 private MinOptMax[] firstPartLengths; 46 private MinOptMax[] secondPartLengths; 47 private MinOptMax[] noBreakLengths; 48 49 private boolean isFirst; 50 private boolean isLast; 51 52 60 public SpaceResolver(List first, BreakElement breakPoss, List second, 61 boolean isFirst, boolean isLast) { 62 this.isFirst = isFirst; 63 this.isLast = isLast; 64 int c = 0; 66 if (first != null) { 67 c += first.size(); 68 } 69 if (second != null) { 70 c += second.size(); 71 } 72 noBreak = new UnresolvedListElementWithLength[c]; 73 noBreakLengths = new MinOptMax[c]; 74 int i = 0; 75 ListIterator iter; 76 if (first != null) { 77 iter = first.listIterator(); 78 while (iter.hasNext()) { 79 noBreak[i] = (UnresolvedListElementWithLength)iter.next(); 80 noBreakLengths[i] = noBreak[i].getLength(); 81 i++; 82 } 83 } 84 if (second != null) { 85 iter = second.listIterator(); 86 while (iter.hasNext()) { 87 noBreak[i] = (UnresolvedListElementWithLength)iter.next(); 88 noBreakLengths[i] = noBreak[i].getLength(); 89 i++; 90 } 91 } 92 93 if (breakPoss != null) { 95 if (breakPoss.getPendingAfterMarks() != null) { 96 if (log.isTraceEnabled()) { 97 log.trace(" adding pending before break: " 98 + breakPoss.getPendingAfterMarks()); 99 } 100 first.addAll(0, breakPoss.getPendingAfterMarks()); 101 } 102 if (breakPoss.getPendingBeforeMarks() != null) { 103 if (log.isTraceEnabled()) { 104 log.trace(" adding pending after break: " 105 + breakPoss.getPendingBeforeMarks()); 106 } 107 second.addAll(0, breakPoss.getPendingBeforeMarks()); 108 } 109 } 110 if (log.isTraceEnabled()) { 111 log.trace("before: " + first); 112 log.trace(" break: " + breakPoss); 113 log.trace("after: " + second); 114 log.trace("NO-BREAK: " + toString(noBreak, noBreakLengths)); 115 } 116 117 if (first != null) { 118 firstPart = new UnresolvedListElementWithLength[first.size()]; 119 firstPartLengths = new MinOptMax[firstPart.length]; 120 first.toArray(firstPart); 121 for (i = 0; i < firstPart.length; i++) { 122 firstPartLengths[i] = firstPart[i].getLength(); 123 } 124 } 125 this.breakPoss = breakPoss; 126 if (second != null) { 127 secondPart = new UnresolvedListElementWithLength[second.size()]; 128 secondPartLengths = new MinOptMax[secondPart.length]; 129 second.toArray(secondPart); 130 for (i = 0; i < secondPart.length; i++) { 131 secondPartLengths[i] = secondPart[i].getLength(); 132 } 133 } 134 resolve(); 135 } 136 137 private String toString(Object [] arr1, Object [] arr2) { 138 if (arr1.length != arr2.length) { 139 new IllegalArgumentException ("The length of both arrays must be equal"); 140 } 141 StringBuffer sb = new StringBuffer ("["); 142 for (int i = 0; i < arr1.length; i++) { 143 if (i > 0) { 144 sb.append(", "); 145 } 146 sb.append(String.valueOf(arr1[i])); 147 sb.append("/"); 148 sb.append(String.valueOf(arr2[i])); 149 } 150 sb.append("]"); 151 return sb.toString(); 152 } 153 154 private void removeConditionalBorderAndPadding( 155 UnresolvedListElement[] elems, MinOptMax[] lengths, boolean reverse) { 156 for (int i = 0; i < elems.length; i++) { 157 int effIndex; 158 if (reverse) { 159 effIndex = elems.length - 1 - i; 160 } else { 161 effIndex = i; 162 } 163 if (elems[effIndex] instanceof BorderOrPaddingElement) { 164 BorderOrPaddingElement bop = (BorderOrPaddingElement)elems[effIndex]; 165 if (bop.isConditional() && !(bop.isFirst() || bop.isLast())) { 166 if (log.isDebugEnabled()) { 167 log.debug("Nulling conditional element: " + bop); 168 } 169 lengths[effIndex] = null; 170 } 171 } 172 } 173 if (log.isTraceEnabled() && elems.length > 0) { 174 log.trace("-->Resulting list: " + toString(elems, lengths)); 175 } 176 } 177 178 private void performSpaceResolutionRule1(UnresolvedListElement[] elems, MinOptMax[] lengths, 179 boolean reverse) { 180 for (int i = 0; i < elems.length; i++) { 181 int effIndex; 182 if (reverse) { 183 effIndex = elems.length - 1 - i; 184 } else { 185 effIndex = i; 186 } 187 if (lengths[effIndex] == null) { 188 continue; 190 } else if (elems[effIndex] instanceof BorderOrPaddingElement) { 191 break; 193 } else if (!elems[effIndex].isConditional()) { 194 break; 195 } 196 if (log.isDebugEnabled()) { 197 log.debug("Nulling conditional element using 4.3.1, rule 1: " + elems[effIndex]); 198 } 199 lengths[effIndex] = null; 200 } 201 if (log.isTraceEnabled() && elems.length > 0) { 202 log.trace("-->Resulting list: " + toString(elems, lengths)); 203 } 204 } 205 206 private void performSpaceResolutionRules2to3(UnresolvedListElement[] elems, 207 MinOptMax[] lengths, int start, int end) { 208 if (log.isTraceEnabled()) { 209 log.trace("rule 2-3: " + start + "-" + end); 210 } 211 SpaceElement space; 212 int remaining; 213 214 boolean hasForcing = false; 216 remaining = 0; 217 for (int i = start; i <= end; i++) { 218 if (lengths[i] == null) { 219 continue; 220 } 221 remaining++; 222 space = (SpaceElement)elems[i]; 223 if (space.isForcing()) { 224 hasForcing = true; 225 break; 226 } 227 } 228 if (remaining == 0) { 229 return; } 231 if (hasForcing) { 232 for (int i = start; i <= end; i++) { 233 if (lengths[i] == null) { 234 continue; 235 } 236 space = (SpaceElement)elems[i]; 237 if (!space.isForcing()) { 238 if (log.isDebugEnabled()) { 239 log.debug("Nulling non-forcing space-specifier using 4.3.1, rule 2: " 240 + elems[i]); 241 } 242 lengths[i] = null; 243 } 244 } 245 return; } 247 248 int highestPrecedence = Integer.MIN_VALUE; 251 for (int i = start; i <= end; i++) { 252 if (lengths[i] == null) { 253 continue; 254 } 255 space = (SpaceElement)elems[i]; 256 highestPrecedence = Math.max(highestPrecedence, space.getPrecedence()); 257 } 258 if (highestPrecedence != 0 && log.isDebugEnabled()) { 259 log.debug("Highest precedence is " + highestPrecedence); 260 } 261 remaining = 0; 263 int greatestOptimum = Integer.MIN_VALUE; 264 for (int i = start; i <= end; i++) { 265 if (lengths[i] == null) { 266 continue; 267 } 268 space = (SpaceElement)elems[i]; 269 if (space.getPrecedence() != highestPrecedence) { 270 if (log.isDebugEnabled()) { 271 log.debug("Nulling space-specifier with precedence " 272 + space.getPrecedence() + " using 4.3.1, rule 3: " 273 + elems[i]); 274 } 275 lengths[i] = null; 276 } else { 277 greatestOptimum = Math.max(greatestOptimum, space.getLength().opt); 278 remaining++; 279 } 280 } 281 if (log.isDebugEnabled()) { 282 log.debug("Greatest optimum: " + greatestOptimum); 283 } 284 if (remaining <= 1) { 285 return; 286 } 287 remaining = 0; 289 for (int i = start; i <= end; i++) { 290 if (lengths[i] == null) { 291 continue; 292 } 293 space = (SpaceElement)elems[i]; 294 if (space.getLength().opt < greatestOptimum) { 295 if (log.isDebugEnabled()) { 296 log.debug("Nulling space-specifier with smaller optimum length " 297 + "using 4.3.1, rule 3: " 298 + elems[i]); 299 } 300 lengths[i] = null; 301 } else { 302 remaining++; 303 } 304 } 305 if (remaining <= 1) { 306 return; 307 } 308 int min = Integer.MIN_VALUE; 310 int max = Integer.MAX_VALUE; 311 for (int i = start; i <= end; i++) { 312 if (lengths[i] == null) { 313 continue; 314 } 315 space = (SpaceElement)elems[i]; 316 min = Math.max(min, space.getLength().min); 317 max = Math.min(max, space.getLength().max); 318 if (remaining > 1) { 319 if (log.isDebugEnabled()) { 320 log.debug("Nulling non-last space-specifier using 4.3.1, rule 3, second part: " 321 + elems[i]); 322 } 323 lengths[i] = null; 324 remaining--; 325 } else { 326 lengths[i].min = min; 327 lengths[i].max = max; 328 } 329 } 330 331 if (log.isTraceEnabled() && elems.length > 0) { 332 log.trace("Remaining spaces: " + remaining); 333 log.trace("-->Resulting list: " + toString(elems, lengths)); 334 } 335 } 336 337 private void performSpaceResolutionRules2to3(UnresolvedListElement[] elems, 338 MinOptMax[] lengths) { 339 int start = 0; 340 int i = start; 341 while (i < elems.length) { 342 if (elems[i] instanceof SpaceElement) { 343 while (i < elems.length) { 344 if (elems[i] == null || elems[i] instanceof SpaceElement) { 345 i++; 346 } else { 347 break; 348 } 349 } 350 performSpaceResolutionRules2to3(elems, lengths, start, i - 1); 351 } 352 i++; 353 start = i; 354 } 355 } 356 357 private boolean hasFirstPart() { 358 return firstPart != null && firstPart.length > 0; 359 } 360 361 private boolean hasSecondPart() { 362 return secondPart != null && secondPart.length > 0; 363 } 364 365 private void resolve() { 366 if (breakPoss != null) { 367 if (hasFirstPart()) { 368 removeConditionalBorderAndPadding(firstPart, firstPartLengths, true); 369 performSpaceResolutionRule1(firstPart, firstPartLengths, true); 370 performSpaceResolutionRules2to3(firstPart, firstPartLengths); 371 } 372 if (hasSecondPart()) { 373 removeConditionalBorderAndPadding(secondPart, secondPartLengths, false); 374 performSpaceResolutionRule1(secondPart, secondPartLengths, false); 375 performSpaceResolutionRules2to3(secondPart, secondPartLengths); 376 } 377 if (noBreak != null) { 378 performSpaceResolutionRules2to3(noBreak, noBreakLengths); 379 } 380 } else { 381 if (isFirst) { 382 removeConditionalBorderAndPadding(secondPart, secondPartLengths, false); 383 performSpaceResolutionRule1(secondPart, secondPartLengths, false); 384 } 385 if (isLast) { 386 removeConditionalBorderAndPadding(firstPart, firstPartLengths, true); 387 performSpaceResolutionRule1(firstPart, firstPartLengths, true); 388 } 389 390 if (hasFirstPart()) { 391 log.trace("Swapping first and second parts."); 394 UnresolvedListElementWithLength[] tempList; 395 MinOptMax[] tempLengths; 396 tempList = secondPart; 397 tempLengths = secondPartLengths; 398 secondPart = firstPart; 399 secondPartLengths = firstPartLengths; 400 firstPart = tempList; 401 firstPartLengths = tempLengths; 402 if (hasFirstPart()) { 403 throw new IllegalStateException ("Didn't expect more than one parts in a" 404 + "no-break condition."); 405 } 406 } 407 performSpaceResolutionRules2to3(secondPart, secondPartLengths); 408 } 409 } 410 411 private MinOptMax sum(MinOptMax[] lengths) { 412 MinOptMax sum = new MinOptMax(); 413 for (int i = 0; i < lengths.length; i++) { 414 if (lengths[i] != null) { 415 sum.add(lengths[i]); 416 } 417 } 418 return sum; 419 } 420 421 private void generate(ListIterator iter) { 422 MinOptMax noBreakLength = new MinOptMax(); 423 MinOptMax glue1; MinOptMax glue3; glue1 = sum(firstPartLengths); 427 glue3 = sum(secondPartLengths); 428 noBreakLength = sum(noBreakLengths); 429 430 435 int glue2w = noBreakLength.opt - glue1.opt - glue3.opt; 436 int glue2stretch = (noBreakLength.max - noBreakLength.opt); 437 int glue2shrink = (noBreakLength.opt - noBreakLength.min); 438 glue2stretch -= glue1.max - glue1.opt; 439 glue2stretch -= glue3.max - glue3.opt; 440 glue2shrink -= glue1.opt - glue1.min; 441 glue2shrink -= glue3.opt - glue3.min; 442 443 boolean hasPrecedingNonBlock = false; 444 boolean forcedBreak = false; 445 if (log.isDebugEnabled()) { 446 log.debug("noBreakLength=" + noBreakLength 447 + ", glue1=" + glue1 448 + ", glue2=" + glue2w + "+" + glue2stretch + "-" + glue2shrink 449 + ", glue3=" + glue3); 450 } 451 if (breakPoss != null) { 452 if (glue1.isNonZero()) { 453 iter.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, 454 false, (Position)null, true)); 455 iter.add(new KnuthGlue(glue1.opt, glue1.max - glue1.opt, glue1.opt - glue1.min, 456 (Position)null, true)); 457 } 458 iter.add(new KnuthPenalty(breakPoss.getPenaltyWidth(), breakPoss.getPenaltyValue(), 459 false, breakPoss.getBreakClass(), 460 new SpaceHandlingBreakPosition(this, breakPoss), false)); 461 if (breakPoss.getPenaltyValue() <= -KnuthPenalty.INFINITE) { 462 return; } 464 if (glue2w != 0 || glue2stretch != 0 || glue2shrink != 0) { 465 iter.add(new KnuthGlue(glue2w, glue2stretch, glue2shrink, 466 (Position)null, true)); 467 } 468 } else { 469 if (glue1.isNonZero()) { 470 throw new IllegalStateException ("glue1 should be 0 in this case"); 471 } 472 } 473 Position pos = null; 474 if (breakPoss == null) { 475 pos = new SpaceHandlingPosition(this); 476 } 477 if (glue3.isNonZero() || pos != null) { 478 iter.add(new KnuthBox(0, pos, true)); 479 } 480 if (glue3.isNonZero()) { 481 iter.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, 482 false, (Position)null, true)); 483 iter.add(new KnuthGlue(glue3.opt, glue3.max - glue3.opt, glue3.opt - glue3.min, 484 (Position)null, true)); 485 hasPrecedingNonBlock = true; 486 } 487 if (isLast && hasPrecedingNonBlock) { 488 iter.add(new KnuthBox(0, (Position)null, true)); 490 } 491 } 492 493 497 public class SpaceHandlingBreakPosition extends Position { 498 499 private SpaceResolver resolver; 500 private Position originalPosition; 501 502 507 public SpaceHandlingBreakPosition(SpaceResolver resolver, BreakElement breakPoss) { 508 super(null); 509 this.resolver = resolver; 510 this.originalPosition = breakPoss.getPosition(); 511 while (this.originalPosition instanceof NonLeafPosition) { 513 this.originalPosition = this.originalPosition.getPosition(); 514 } 515 } 516 517 518 public SpaceResolver getSpaceResolver() { 519 return this.resolver; 520 } 521 522 529 public void notifyBreakSituation(boolean isBreakSituation, RelSide side) { 530 if (isBreakSituation) { 531 if (RelSide.BEFORE == side) { 532 for (int i = 0; i < resolver.secondPart.length; i++) { 533 resolver.secondPart[i].notifyLayoutManager(resolver.secondPartLengths[i]); 534 } 535 } else { 536 for (int i = 0; i < resolver.firstPart.length; i++) { 537 resolver.firstPart[i].notifyLayoutManager(resolver.firstPartLengths[i]); 538 } 539 } 540 } else { 541 for (int i = 0; i < resolver.noBreak.length; i++) { 542 resolver.noBreak[i].notifyLayoutManager(resolver.noBreakLengths[i]); 543 } 544 } 545 } 546 547 548 public String toString() { 549 StringBuffer sb = new StringBuffer (); 550 sb.append("SpaceHandlingBreakPosition("); 551 sb.append(this.originalPosition); 552 sb.append(")"); 553 return sb.toString(); 554 } 555 556 560 public Position getOriginalBreakPosition() { 561 return this.originalPosition; 562 } 563 } 564 565 569 public class SpaceHandlingPosition extends Position { 570 571 private SpaceResolver resolver; 572 573 577 public SpaceHandlingPosition(SpaceResolver resolver) { 578 super(null); 579 this.resolver = resolver; 580 } 581 582 583 public SpaceResolver getSpaceResolver() { 584 return this.resolver; 585 } 586 587 591 public void notifySpaceSituation() { 592 if (resolver.breakPoss != null) { 593 throw new IllegalStateException ("Only applicable to no-break situations"); 594 } 595 for (int i = 0; i < resolver.secondPart.length; i++) { 596 resolver.secondPart[i].notifyLayoutManager(resolver.secondPartLengths[i]); 597 } 598 } 599 600 601 public String toString() { 602 StringBuffer sb = new StringBuffer (); 603 sb.append("SpaceHandlingPosition"); 604 return sb.toString(); 605 } 606 } 607 608 612 public static void resolveElementList(LinkedList elems) { 613 if (log.isTraceEnabled()) { 614 log.trace(elems); 615 } 616 boolean first = true; 617 boolean last = false; 618 boolean skipNextElement = false; 619 List unresolvedFirst = new java.util.ArrayList (); 620 List unresolvedSecond = new java.util.ArrayList (); 621 List currentGroup; 622 ListIterator iter = elems.listIterator(); 623 while (iter.hasNext()) { 624 ListElement el = (ListElement)iter.next(); 625 if (el.isUnresolvedElement()) { 626 if (log.isTraceEnabled()) { 627 log.trace("unresolved found: " + el + " " + first + "/" + last); 628 } 629 BreakElement breakPoss = null; 630 unresolvedFirst.clear(); 632 unresolvedSecond.clear(); 633 if (el instanceof BreakElement) { 635 breakPoss = (BreakElement)el; 636 currentGroup = unresolvedSecond; 637 } else { 638 currentGroup = unresolvedFirst; 639 currentGroup.add(el); 640 } 641 iter.remove(); 642 last = true; 643 skipNextElement = true; 644 while (iter.hasNext()) { 645 el = (ListElement)iter.next(); 646 if (el instanceof BreakElement && breakPoss != null) { 647 skipNextElement = false; 648 last = false; 649 break; 650 } else if (currentGroup == unresolvedFirst && (el instanceof BreakElement)) { 651 breakPoss = (BreakElement)el; 652 iter.remove(); 653 currentGroup = unresolvedSecond; 654 } else if (el.isUnresolvedElement()) { 655 currentGroup.add(el); 656 iter.remove(); 657 } else { 658 last = false; 659 break; 660 } 661 } 662 if (breakPoss == null && unresolvedSecond.size() == 0 && !last) { 664 log.trace("Swap first and second parts in no-break condition," 665 + " second part is empty."); 666 List swapList = unresolvedSecond; 668 unresolvedSecond = unresolvedFirst; 669 unresolvedFirst = swapList; 670 } 671 672 log.debug("----start space resolution (first=" + first + ", last=" + last + ")..."); 673 SpaceResolver resolver = new SpaceResolver( 674 unresolvedFirst, breakPoss, unresolvedSecond, first, last); 675 if (!last) { 676 iter.previous(); 677 } 678 resolver.generate(iter); 679 if (!last && skipNextElement) { 680 iter.next(); 681 } 682 log.debug("----end space resolution."); 683 } 684 first = false; 685 } 686 } 687 688 697 public static void performConditionalsNotification(List effectiveList, 698 int startElementIndex, int endElementIndex, int prevBreak) { 699 KnuthElement el = null; 700 if (prevBreak > 0) { 701 el = (KnuthElement)effectiveList.get(prevBreak); 702 } 703 SpaceResolver.SpaceHandlingBreakPosition beforeBreak = null; 704 SpaceResolver.SpaceHandlingBreakPosition afterBreak = null; 705 if (el != null && el.isPenalty()) { 706 Position pos = el.getPosition(); 707 if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) { 708 beforeBreak = (SpaceResolver.SpaceHandlingBreakPosition)pos; 709 beforeBreak.notifyBreakSituation(true, RelSide.BEFORE); 710 } 711 } 712 el = (KnuthElement)effectiveList.get(endElementIndex); 713 if (el != null && el.isPenalty()) { 714 Position pos = el.getPosition(); 715 if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) { 716 afterBreak = (SpaceResolver.SpaceHandlingBreakPosition)pos; 717 afterBreak.notifyBreakSituation(true, RelSide.AFTER); 718 } 719 } 720 for (int i = startElementIndex; i <= endElementIndex; i++) { 721 Position pos = ((KnuthElement)effectiveList.get(i)).getPosition(); 722 if (pos instanceof SpaceResolver.SpaceHandlingPosition) { 723 ((SpaceResolver.SpaceHandlingPosition)pos).notifySpaceSituation(); 724 } else if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) { 725 SpaceResolver.SpaceHandlingBreakPosition noBreak; 726 noBreak = (SpaceResolver.SpaceHandlingBreakPosition)pos; 727 if (noBreak != beforeBreak && noBreak != afterBreak) { 728 noBreak.notifyBreakSituation(false, null); 729 } 730 } 731 } 732 } 733 734 735 736 } 737 | Popular Tags |