1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* $Id: SpaceResolver.java 1805173 2017-08-16 10:50:04Z ssteiner $ */ 19 20 package org.apache.fop.layoutmgr; 21 22 import java.util.List; 23 import java.util.ListIterator; 24 25 import org.apache.commons.logging.Log; 26 import org.apache.commons.logging.LogFactory; 27 28 import org.apache.fop.traits.MinOptMax; 29 30 /** 31 * This class resolves spaces and conditional borders and paddings by replacing the 32 * UnresolvedListElements descendants by the right combination of KnuthElements on an element 33 * list. 34 */ 35 public final class SpaceResolver { 36 37 /** Logger instance */ 38 private static final 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 /** 53 * Main constructor. 54 * @param first Element list before a break (optional) 55 * @param breakPoss Break possibility (optional) 56 * @param second Element list after a break (or if no break possibility in vicinity) 57 * @param isFirst Resolution at the beginning of a (full) element list 58 * @param isLast Resolution at the end of a (full) element list 59 */ SpaceResolver(List first, BreakElement breakPoss, List second, boolean isFirst, boolean isLast)60 private SpaceResolver(List first, BreakElement breakPoss, List second, 61 boolean isFirst, boolean isLast) { 62 this.isFirst = isFirst; 63 this.isLast = isLast; 64 //Create combined no-break list 65 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 //Add pending elements from higher level FOs 94 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 toString(Object[] arr1, Object[] arr2)137 private String toString(Object[] arr1, Object[] arr2) { 138 if (arr1.length != arr2.length) { 139 throw 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 removeConditionalBorderAndPadding( UnresolvedListElement[] elems, MinOptMax[] lengths, boolean reverse)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 performSpaceResolutionRule1(UnresolvedListElement[] elems, MinOptMax[] lengths, boolean reverse)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 //Zeroed border or padding doesn't create a fence 189 continue; 190 } else if (elems[effIndex] instanceof BorderOrPaddingElement) { 191 //Border or padding form fences! 192 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 performSpaceResolutionRules2to3(UnresolvedListElement[] elems, MinOptMax[] lengths, int start, int end)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 //Rule 2 (4.3.1, XSL 1.0) 215 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; //shortcut 230 } 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; //If rule is triggered skip rule 3 246 } 247 248 //Rule 3 (4.3.1, XSL 1.0) 249 //Determine highes precedence 250 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 //Suppress space-specifiers with lower precedence 262 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().getOpt()); 278 remaining++; 279 } 280 } 281 if (LOG.isDebugEnabled()) { 282 LOG.debug("Greatest optimum: " + greatestOptimum); 283 } 284 if (remaining <= 1) { 285 return; 286 } 287 //Suppress space-specifiers with smaller optimum length 288 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().getOpt() < 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 //Construct resolved space-specifier from the remaining spaces 309 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().getMin()); 317 max = Math.min(max, space.getLength().getMax()); 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] = MinOptMax.getInstance(min, lengths[i].getOpt(), max); 327 } 328 } 329 330 if (LOG.isTraceEnabled() && elems.length > 0) { 331 LOG.trace("Remaining spaces: " + remaining); 332 LOG.trace("-->Resulting list: " + toString(elems, lengths)); 333 } 334 } 335 performSpaceResolutionRules2to3(UnresolvedListElement[] elems, MinOptMax[] lengths)336 private void performSpaceResolutionRules2to3(UnresolvedListElement[] elems, 337 MinOptMax[] lengths) { 338 int start = 0; 339 int i = start; 340 while (i < elems.length) { 341 if (elems[i] instanceof SpaceElement) { 342 while (i < elems.length) { 343 if (elems[i] == null || elems[i] instanceof SpaceElement) { 344 i++; 345 } else { 346 break; 347 } 348 } 349 performSpaceResolutionRules2to3(elems, lengths, start, i - 1); 350 } 351 i++; 352 start = i; 353 } 354 } 355 hasFirstPart()356 private boolean hasFirstPart() { 357 return firstPart != null && firstPart.length > 0; 358 } 359 hasSecondPart()360 private boolean hasSecondPart() { 361 return secondPart != null && secondPart.length > 0; 362 } 363 resolve()364 private void resolve() { 365 if (breakPoss != null) { 366 if (hasFirstPart()) { 367 removeConditionalBorderAndPadding(firstPart, firstPartLengths, true); 368 performSpaceResolutionRule1(firstPart, firstPartLengths, true); 369 performSpaceResolutionRules2to3(firstPart, firstPartLengths); 370 } 371 if (hasSecondPart()) { 372 removeConditionalBorderAndPadding(secondPart, secondPartLengths, false); 373 performSpaceResolutionRule1(secondPart, secondPartLengths, false); 374 performSpaceResolutionRules2to3(secondPart, secondPartLengths); 375 } 376 if (noBreak != null) { 377 performSpaceResolutionRules2to3(noBreak, noBreakLengths); 378 } 379 } else { 380 if (isFirst) { 381 removeConditionalBorderAndPadding(secondPart, secondPartLengths, false); 382 performSpaceResolutionRule1(secondPart, secondPartLengths, false); 383 } 384 if (isLast) { 385 removeConditionalBorderAndPadding(firstPart, firstPartLengths, true); 386 performSpaceResolutionRule1(firstPart, firstPartLengths, true); 387 } 388 389 if (hasFirstPart()) { 390 //Now that we've handled isFirst/isLast conditions, we need to look at the 391 //active part in its normal order so swap it back. 392 LOG.trace("Swapping first and second parts."); 393 UnresolvedListElementWithLength[] tempList; 394 MinOptMax[] tempLengths; 395 tempList = secondPart; 396 tempLengths = secondPartLengths; 397 secondPart = firstPart; 398 secondPartLengths = firstPartLengths; 399 firstPart = tempList; 400 firstPartLengths = tempLengths; 401 if (hasFirstPart()) { 402 throw new IllegalStateException("Didn't expect more than one parts in a" 403 + "no-break condition."); 404 } 405 } 406 performSpaceResolutionRules2to3(secondPart, secondPartLengths); 407 } 408 } 409 sum(MinOptMax[] lengths)410 private MinOptMax sum(MinOptMax[] lengths) { 411 MinOptMax sum = MinOptMax.ZERO; 412 for (MinOptMax length : lengths) { 413 if (length != null) { 414 sum = sum.plus(length); 415 } 416 } 417 return sum; 418 } 419 generate(ListIterator iter)420 private void generate(ListIterator iter) { 421 MinOptMax spaceBeforeBreak = sum(firstPartLengths); 422 MinOptMax spaceAfterBreak = sum(secondPartLengths); 423 424 boolean hasPrecedingNonBlock = false; 425 if (breakPoss != null) { 426 if (spaceBeforeBreak.isNonZero()) { 427 iter.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false, null, true)); 428 iter.add(new KnuthGlue(spaceBeforeBreak, null, true)); 429 if (breakPoss.isForcedBreak()) { 430 //Otherwise, the preceding penalty and glue will be cut off 431 iter.add(new KnuthBox(0, null, true)); 432 } 433 } 434 iter.add(new KnuthPenalty(breakPoss.getPenaltyWidth(), breakPoss.getPenaltyValue(), 435 false, breakPoss.getBreakClass(), 436 new SpaceHandlingBreakPosition(this, breakPoss), false)); 437 if (breakPoss.getPenaltyValue() <= -KnuthPenalty.INFINITE) { 438 return; //return early. Not necessary (even wrong) to add additional elements 439 } 440 441 // No break 442 // TODO: We can't use a MinOptMax for glue2, 443 // because min <= opt <= max is not always true - why? 444 MinOptMax noBreakLength = sum(noBreakLengths); 445 MinOptMax spaceSum = spaceBeforeBreak.plus(spaceAfterBreak); 446 int glue2width = noBreakLength.getOpt() - spaceSum.getOpt(); 447 int glue2stretch = noBreakLength.getStretch() - spaceSum.getStretch(); 448 int glue2shrink = noBreakLength.getShrink() - spaceSum.getShrink(); 449 450 if (glue2width != 0 || glue2stretch != 0 || glue2shrink != 0) { 451 iter.add(new KnuthGlue(glue2width, glue2stretch, glue2shrink, null, true)); 452 } 453 } else { 454 if (spaceBeforeBreak.isNonZero()) { 455 throw new IllegalStateException("spaceBeforeBreak should be 0 in this case"); 456 } 457 } 458 Position pos = null; 459 if (breakPoss == null) { 460 pos = new SpaceHandlingPosition(this); 461 } 462 if (spaceAfterBreak.isNonZero() || pos != null) { 463 iter.add(new KnuthBox(0, pos, true)); 464 } 465 if (spaceAfterBreak.isNonZero()) { 466 iter.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false, null, true)); 467 iter.add(new KnuthGlue(spaceAfterBreak, null, true)); 468 hasPrecedingNonBlock = true; 469 } 470 if (isLast && hasPrecedingNonBlock) { 471 //Otherwise, the preceding penalty and glue will be cut off 472 iter.add(new KnuthBox(0, null, true)); 473 } 474 } 475 476 /** 477 * Position class for break possibilities. It is used to notify layout manager about the 478 * effective spaces and conditional lengths. 479 */ 480 public static class SpaceHandlingBreakPosition extends Position { 481 482 private SpaceResolver resolver; 483 private Position originalPosition; 484 485 /** 486 * Main constructor. 487 * @param resolver the space resolver that provides the info about the actual situation 488 * @param breakPoss the original break possibility that creates this Position 489 */ SpaceHandlingBreakPosition(SpaceResolver resolver, BreakElement breakPoss)490 public SpaceHandlingBreakPosition(SpaceResolver resolver, BreakElement breakPoss) { 491 super(null); 492 this.resolver = resolver; 493 this.originalPosition = breakPoss.getPosition(); 494 //Unpack since the SpaceHandlingBreakPosition is a non-wrapped Position, too 495 while (this.originalPosition instanceof NonLeafPosition) { 496 this.originalPosition = this.originalPosition.getPosition(); 497 } 498 } 499 500 /** @return the space resolver */ getSpaceResolver()501 public SpaceResolver getSpaceResolver() { 502 return this.resolver; 503 } 504 505 /** 506 * Notifies all affected layout managers about the current situation in the part to be 507 * handled for area generation. 508 * @param isBreakSituation true if this is a break situation. 509 * @param side defines to notify about the situation whether before or after the break. 510 * May be null if isBreakSituation is null. 511 */ notifyBreakSituation(boolean isBreakSituation, RelSide side)512 public void notifyBreakSituation(boolean isBreakSituation, RelSide side) { 513 if (isBreakSituation) { 514 if (RelSide.BEFORE == side) { 515 for (int i = 0; i < resolver.secondPart.length; i++) { 516 resolver.secondPart[i].notifyLayoutManager(resolver.secondPartLengths[i]); 517 } 518 } else { 519 for (int i = 0; i < resolver.firstPart.length; i++) { 520 resolver.firstPart[i].notifyLayoutManager(resolver.firstPartLengths[i]); 521 } 522 } 523 } else { 524 for (int i = 0; i < resolver.noBreak.length; i++) { 525 resolver.noBreak[i].notifyLayoutManager(resolver.noBreakLengths[i]); 526 } 527 } 528 } 529 530 /** {@inheritDoc} */ toString()531 public String toString() { 532 StringBuffer sb = new StringBuffer(); 533 sb.append("SpaceHandlingBreakPosition("); 534 sb.append(this.originalPosition); 535 sb.append(")"); 536 return sb.toString(); 537 } 538 539 /** 540 * @return the original Position instance set at the BreakElement that this Position was 541 * created for. 542 */ getOriginalBreakPosition()543 public Position getOriginalBreakPosition() { 544 return this.originalPosition; 545 } 546 547 /** {@inheritDoc} */ getPosition()548 public Position getPosition() { 549 return originalPosition; 550 } 551 552 } 553 554 /** 555 * Position class for no-break situations. It is used to notify layout manager about the 556 * effective spaces and conditional lengths. 557 */ 558 public static class SpaceHandlingPosition extends Position { 559 560 private SpaceResolver resolver; 561 562 /** 563 * Main constructor. 564 * @param resolver the space resolver that provides the info about the actual situation 565 */ SpaceHandlingPosition(SpaceResolver resolver)566 public SpaceHandlingPosition(SpaceResolver resolver) { 567 super(null); 568 this.resolver = resolver; 569 } 570 571 /** @return the space resolver */ getSpaceResolver()572 public SpaceResolver getSpaceResolver() { 573 return this.resolver; 574 } 575 576 /** 577 * Notifies all affected layout managers about the current situation in the part to be 578 * handled for area generation. 579 */ notifySpaceSituation()580 public void notifySpaceSituation() { 581 if (resolver.breakPoss != null) { 582 throw new IllegalStateException("Only applicable to no-break situations"); 583 } 584 for (int i = 0; i < resolver.secondPart.length; i++) { 585 resolver.secondPart[i].notifyLayoutManager(resolver.secondPartLengths[i]); 586 } 587 } 588 589 /** {@inheritDoc} */ toString()590 public String toString() { 591 return "SpaceHandlingPosition"; 592 } 593 } 594 595 /** 596 * Resolves unresolved elements applying the space resolution rules defined in 4.3.1. 597 * @param elems the element list 598 */ resolveElementList(List elems)599 public static void resolveElementList(List elems) { 600 if (LOG.isTraceEnabled()) { 601 LOG.trace(elems); 602 } 603 boolean first = true; 604 boolean last = false; 605 boolean skipNextElement = false; 606 List unresolvedFirst = new java.util.ArrayList(); 607 List unresolvedSecond = new java.util.ArrayList(); 608 List currentGroup; 609 ListIterator iter = elems.listIterator(); 610 while (iter.hasNext()) { 611 ListElement el = (ListElement)iter.next(); 612 if (el.isUnresolvedElement()) { 613 if (LOG.isTraceEnabled()) { 614 LOG.trace("unresolved found: " + el + " " + first + "/" + last); 615 } 616 BreakElement breakPoss = null; 617 //Clear temp lists 618 unresolvedFirst.clear(); 619 unresolvedSecond.clear(); 620 //Collect groups 621 if (el instanceof BreakElement) { 622 breakPoss = (BreakElement)el; 623 currentGroup = unresolvedSecond; 624 } else { 625 currentGroup = unresolvedFirst; 626 currentGroup.add(el); 627 } 628 iter.remove(); 629 last = true; 630 skipNextElement = true; 631 while (iter.hasNext()) { 632 el = (ListElement)iter.next(); 633 if (el instanceof BreakElement && breakPoss != null) { 634 skipNextElement = false; 635 last = false; 636 break; 637 } else if (currentGroup == unresolvedFirst && (el instanceof BreakElement)) { 638 breakPoss = (BreakElement)el; 639 iter.remove(); 640 currentGroup = unresolvedSecond; 641 } else if (el.isUnresolvedElement()) { 642 currentGroup.add(el); 643 iter.remove(); 644 } else { 645 last = false; 646 break; 647 } 648 } 649 //last = !iter.hasNext(); 650 if (breakPoss == null && unresolvedSecond.isEmpty() && !last) { 651 LOG.trace("Swap first and second parts in no-break condition," 652 + " second part is empty."); 653 //The first list is reversed, so swap if this shouldn't happen 654 List swapList = unresolvedSecond; 655 unresolvedSecond = unresolvedFirst; 656 unresolvedFirst = swapList; 657 } 658 659 LOG.debug("----start space resolution (first=" + first + ", last=" + last + ")..."); 660 SpaceResolver resolver = new SpaceResolver( 661 unresolvedFirst, breakPoss, unresolvedSecond, first, last); 662 if (!last) { 663 iter.previous(); 664 } 665 resolver.generate(iter); 666 if (!last && skipNextElement) { 667 iter.next(); 668 } 669 LOG.debug("----end space resolution."); 670 } 671 first = false; 672 } 673 } 674 675 /** 676 * Inspects an effective element list and notifies all layout managers about the state of 677 * the spaces and conditional lengths. 678 * @param effectiveList the effective element list 679 * @param startElementIndex index of the first element in the part to be processed 680 * @param endElementIndex index of the last element in the part to be processed 681 * @param prevBreak index of the the break possibility just before this part (used to 682 * identify a break condition, lastBreak <= 0 represents a no-break condition) 683 */ performConditionalsNotification(List effectiveList, int startElementIndex, int endElementIndex, int prevBreak)684 public static void performConditionalsNotification(List effectiveList, 685 int startElementIndex, int endElementIndex, int prevBreak) { 686 KnuthElement el = null; 687 if (prevBreak > 0) { 688 el = (KnuthElement)effectiveList.get(prevBreak); 689 } 690 SpaceResolver.SpaceHandlingBreakPosition beforeBreak = null; 691 SpaceResolver.SpaceHandlingBreakPosition afterBreak = null; 692 if (el != null && el.isPenalty()) { 693 Position pos = el.getPosition(); 694 if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) { 695 beforeBreak = (SpaceResolver.SpaceHandlingBreakPosition)pos; 696 beforeBreak.notifyBreakSituation(true, RelSide.BEFORE); 697 } 698 } 699 el = endElementIndex > -1 ? (KnuthElement) effectiveList.get(endElementIndex) : null; 700 if (el != null && el.isPenalty()) { 701 Position pos = el.getPosition(); 702 if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) { 703 afterBreak = (SpaceResolver.SpaceHandlingBreakPosition)pos; 704 afterBreak.notifyBreakSituation(true, RelSide.AFTER); 705 } 706 } 707 for (int i = startElementIndex; i <= endElementIndex; i++) { 708 Position pos = ((KnuthElement)effectiveList.get(i)).getPosition(); 709 if (pos instanceof SpaceResolver.SpaceHandlingPosition) { 710 ((SpaceResolver.SpaceHandlingPosition)pos).notifySpaceSituation(); 711 } else if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) { 712 SpaceResolver.SpaceHandlingBreakPosition noBreak; 713 noBreak = (SpaceResolver.SpaceHandlingBreakPosition)pos; 714 if (noBreak != beforeBreak && noBreak != afterBreak) { 715 noBreak.notifyBreakSituation(false, null); 716 } 717 } 718 } 719 } 720 721 722 } 723