You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SpaceResolver.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  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. /* $Id$ */
  18. package org.apache.fop.layoutmgr;
  19. import java.util.List;
  20. import java.util.ListIterator;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. import org.apache.fop.traits.MinOptMax;
  24. /**
  25. * This class resolves spaces and conditional borders and paddings by replacing the
  26. * UnresolvedListElements descendants by the right combination of KnuthElements on an element
  27. * list.
  28. */
  29. public final class SpaceResolver {
  30. /** Logger instance */
  31. private static final Log LOG = LogFactory.getLog(SpaceResolver.class);
  32. private UnresolvedListElementWithLength[] firstPart;
  33. private BreakElement breakPoss;
  34. private UnresolvedListElementWithLength[] secondPart;
  35. private UnresolvedListElementWithLength[] noBreak;
  36. private MinOptMax[] firstPartLengths;
  37. private MinOptMax[] secondPartLengths;
  38. private MinOptMax[] noBreakLengths;
  39. private boolean isFirst;
  40. private boolean isLast;
  41. /**
  42. * Main constructor.
  43. * @param first Element list before a break (optional)
  44. * @param breakPoss Break possibility (optional)
  45. * @param second Element list after a break (or if no break possibility in vicinity)
  46. * @param isFirst Resolution at the beginning of a (full) element list
  47. * @param isLast Resolution at the end of a (full) element list
  48. */
  49. private SpaceResolver(List first, BreakElement breakPoss, List second,
  50. boolean isFirst, boolean isLast) {
  51. this.isFirst = isFirst;
  52. this.isLast = isLast;
  53. //Create combined no-break list
  54. int c = 0;
  55. if (first != null) {
  56. c += first.size();
  57. }
  58. if (second != null) {
  59. c += second.size();
  60. }
  61. noBreak = new UnresolvedListElementWithLength[c];
  62. noBreakLengths = new MinOptMax[c];
  63. int i = 0;
  64. ListIterator iter;
  65. if (first != null) {
  66. iter = first.listIterator();
  67. while (iter.hasNext()) {
  68. noBreak[i] = (UnresolvedListElementWithLength)iter.next();
  69. noBreakLengths[i] = noBreak[i].getLength();
  70. i++;
  71. }
  72. }
  73. if (second != null) {
  74. iter = second.listIterator();
  75. while (iter.hasNext()) {
  76. noBreak[i] = (UnresolvedListElementWithLength)iter.next();
  77. noBreakLengths[i] = noBreak[i].getLength();
  78. i++;
  79. }
  80. }
  81. //Add pending elements from higher level FOs
  82. if (breakPoss != null) {
  83. if (breakPoss.getPendingAfterMarks() != null) {
  84. if (LOG.isTraceEnabled()) {
  85. LOG.trace(" adding pending before break: "
  86. + breakPoss.getPendingAfterMarks());
  87. }
  88. first.addAll(0, breakPoss.getPendingAfterMarks());
  89. }
  90. if (breakPoss.getPendingBeforeMarks() != null) {
  91. if (LOG.isTraceEnabled()) {
  92. LOG.trace(" adding pending after break: "
  93. + breakPoss.getPendingBeforeMarks());
  94. }
  95. second.addAll(0, breakPoss.getPendingBeforeMarks());
  96. }
  97. }
  98. if (LOG.isTraceEnabled()) {
  99. LOG.trace("before: " + first);
  100. LOG.trace(" break: " + breakPoss);
  101. LOG.trace("after: " + second);
  102. LOG.trace("NO-BREAK: " + toString(noBreak, noBreakLengths));
  103. }
  104. if (first != null) {
  105. firstPart = new UnresolvedListElementWithLength[first.size()];
  106. firstPartLengths = new MinOptMax[firstPart.length];
  107. first.toArray(firstPart);
  108. for (i = 0; i < firstPart.length; i++) {
  109. firstPartLengths[i] = firstPart[i].getLength();
  110. }
  111. }
  112. this.breakPoss = breakPoss;
  113. if (second != null) {
  114. secondPart = new UnresolvedListElementWithLength[second.size()];
  115. secondPartLengths = new MinOptMax[secondPart.length];
  116. second.toArray(secondPart);
  117. for (i = 0; i < secondPart.length; i++) {
  118. secondPartLengths[i] = secondPart[i].getLength();
  119. }
  120. }
  121. resolve();
  122. }
  123. private String toString(Object[] arr1, Object[] arr2) {
  124. if (arr1.length != arr2.length) {
  125. throw new IllegalArgumentException("The length of both arrays must be equal");
  126. }
  127. StringBuffer sb = new StringBuffer("[");
  128. for (int i = 0; i < arr1.length; i++) {
  129. if (i > 0) {
  130. sb.append(", ");
  131. }
  132. sb.append(String.valueOf(arr1[i]));
  133. sb.append("/");
  134. sb.append(String.valueOf(arr2[i]));
  135. }
  136. sb.append("]");
  137. return sb.toString();
  138. }
  139. private void removeConditionalBorderAndPadding(
  140. UnresolvedListElement[] elems, MinOptMax[] lengths, boolean reverse) {
  141. for (int i = 0; i < elems.length; i++) {
  142. int effIndex;
  143. if (reverse) {
  144. effIndex = elems.length - 1 - i;
  145. } else {
  146. effIndex = i;
  147. }
  148. if (elems[effIndex] instanceof BorderOrPaddingElement) {
  149. BorderOrPaddingElement bop = (BorderOrPaddingElement)elems[effIndex];
  150. if (bop.isConditional() && !(bop.isFirst() || bop.isLast())) {
  151. if (LOG.isDebugEnabled()) {
  152. LOG.debug("Nulling conditional element: " + bop);
  153. }
  154. lengths[effIndex] = null;
  155. }
  156. }
  157. }
  158. if (LOG.isTraceEnabled() && elems.length > 0) {
  159. LOG.trace("-->Resulting list: " + toString(elems, lengths));
  160. }
  161. }
  162. private void performSpaceResolutionRule1(UnresolvedListElement[] elems, MinOptMax[] lengths,
  163. boolean reverse) {
  164. for (int i = 0; i < elems.length; i++) {
  165. int effIndex;
  166. if (reverse) {
  167. effIndex = elems.length - 1 - i;
  168. } else {
  169. effIndex = i;
  170. }
  171. if (lengths[effIndex] == null) {
  172. //Zeroed border or padding doesn't create a fence
  173. continue;
  174. } else if (elems[effIndex] instanceof BorderOrPaddingElement) {
  175. //Border or padding form fences!
  176. break;
  177. } else if (!elems[effIndex].isConditional()) {
  178. break;
  179. }
  180. if (LOG.isDebugEnabled()) {
  181. LOG.debug("Nulling conditional element using 4.3.1, rule 1: " + elems[effIndex]);
  182. }
  183. lengths[effIndex] = null;
  184. }
  185. if (LOG.isTraceEnabled() && elems.length > 0) {
  186. LOG.trace("-->Resulting list: " + toString(elems, lengths));
  187. }
  188. }
  189. private void performSpaceResolutionRules2to3(UnresolvedListElement[] elems,
  190. MinOptMax[] lengths, int start, int end) {
  191. if (LOG.isTraceEnabled()) {
  192. LOG.trace("rule 2-3: " + start + "-" + end);
  193. }
  194. SpaceElement space;
  195. int remaining;
  196. //Rule 2 (4.3.1, XSL 1.0)
  197. boolean hasForcing = false;
  198. remaining = 0;
  199. for (int i = start; i <= end; i++) {
  200. if (lengths[i] == null) {
  201. continue;
  202. }
  203. remaining++;
  204. space = (SpaceElement)elems[i];
  205. if (space.isForcing()) {
  206. hasForcing = true;
  207. break;
  208. }
  209. }
  210. if (remaining == 0) {
  211. return; //shortcut
  212. }
  213. if (hasForcing) {
  214. for (int i = start; i <= end; i++) {
  215. if (lengths[i] == null) {
  216. continue;
  217. }
  218. space = (SpaceElement)elems[i];
  219. if (!space.isForcing()) {
  220. if (LOG.isDebugEnabled()) {
  221. LOG.debug("Nulling non-forcing space-specifier using 4.3.1, rule 2: "
  222. + elems[i]);
  223. }
  224. lengths[i] = null;
  225. }
  226. }
  227. return; //If rule is triggered skip rule 3
  228. }
  229. //Rule 3 (4.3.1, XSL 1.0)
  230. //Determine highes precedence
  231. int highestPrecedence = Integer.MIN_VALUE;
  232. for (int i = start; i <= end; i++) {
  233. if (lengths[i] == null) {
  234. continue;
  235. }
  236. space = (SpaceElement)elems[i];
  237. highestPrecedence = Math.max(highestPrecedence, space.getPrecedence());
  238. }
  239. if (highestPrecedence != 0 && LOG.isDebugEnabled()) {
  240. LOG.debug("Highest precedence is " + highestPrecedence);
  241. }
  242. //Suppress space-specifiers with lower precedence
  243. remaining = 0;
  244. int greatestOptimum = Integer.MIN_VALUE;
  245. for (int i = start; i <= end; i++) {
  246. if (lengths[i] == null) {
  247. continue;
  248. }
  249. space = (SpaceElement)elems[i];
  250. if (space.getPrecedence() != highestPrecedence) {
  251. if (LOG.isDebugEnabled()) {
  252. LOG.debug("Nulling space-specifier with precedence "
  253. + space.getPrecedence() + " using 4.3.1, rule 3: "
  254. + elems[i]);
  255. }
  256. lengths[i] = null;
  257. } else {
  258. greatestOptimum = Math.max(greatestOptimum, space.getLength().getOpt());
  259. remaining++;
  260. }
  261. }
  262. if (LOG.isDebugEnabled()) {
  263. LOG.debug("Greatest optimum: " + greatestOptimum);
  264. }
  265. if (remaining <= 1) {
  266. return;
  267. }
  268. //Suppress space-specifiers with smaller optimum length
  269. remaining = 0;
  270. for (int i = start; i <= end; i++) {
  271. if (lengths[i] == null) {
  272. continue;
  273. }
  274. space = (SpaceElement)elems[i];
  275. if (space.getLength().getOpt() < greatestOptimum) {
  276. if (LOG.isDebugEnabled()) {
  277. LOG.debug("Nulling space-specifier with smaller optimum length "
  278. + "using 4.3.1, rule 3: "
  279. + elems[i]);
  280. }
  281. lengths[i] = null;
  282. } else {
  283. remaining++;
  284. }
  285. }
  286. if (remaining <= 1) {
  287. return;
  288. }
  289. //Construct resolved space-specifier from the remaining spaces
  290. int min = Integer.MIN_VALUE;
  291. int max = Integer.MAX_VALUE;
  292. for (int i = start; i <= end; i++) {
  293. if (lengths[i] == null) {
  294. continue;
  295. }
  296. space = (SpaceElement)elems[i];
  297. min = Math.max(min, space.getLength().getMin());
  298. max = Math.min(max, space.getLength().getMax());
  299. if (remaining > 1) {
  300. if (LOG.isDebugEnabled()) {
  301. LOG.debug("Nulling non-last space-specifier using 4.3.1, rule 3, second part: "
  302. + elems[i]);
  303. }
  304. lengths[i] = null;
  305. remaining--;
  306. } else {
  307. lengths[i] = MinOptMax.getInstance(min, lengths[i].getOpt(), max);
  308. }
  309. }
  310. if (LOG.isTraceEnabled() && elems.length > 0) {
  311. LOG.trace("Remaining spaces: " + remaining);
  312. LOG.trace("-->Resulting list: " + toString(elems, lengths));
  313. }
  314. }
  315. private void performSpaceResolutionRules2to3(UnresolvedListElement[] elems,
  316. MinOptMax[] lengths) {
  317. int start = 0;
  318. int i = start;
  319. while (i < elems.length) {
  320. if (elems[i] instanceof SpaceElement) {
  321. while (i < elems.length) {
  322. if (elems[i] == null || elems[i] instanceof SpaceElement) {
  323. i++;
  324. } else {
  325. break;
  326. }
  327. }
  328. performSpaceResolutionRules2to3(elems, lengths, start, i - 1);
  329. }
  330. i++;
  331. start = i;
  332. }
  333. }
  334. private boolean hasFirstPart() {
  335. return firstPart != null && firstPart.length > 0;
  336. }
  337. private boolean hasSecondPart() {
  338. return secondPart != null && secondPart.length > 0;
  339. }
  340. private void resolve() {
  341. if (breakPoss != null) {
  342. if (hasFirstPart()) {
  343. removeConditionalBorderAndPadding(firstPart, firstPartLengths, true);
  344. performSpaceResolutionRule1(firstPart, firstPartLengths, true);
  345. performSpaceResolutionRules2to3(firstPart, firstPartLengths);
  346. }
  347. if (hasSecondPart()) {
  348. removeConditionalBorderAndPadding(secondPart, secondPartLengths, false);
  349. performSpaceResolutionRule1(secondPart, secondPartLengths, false);
  350. performSpaceResolutionRules2to3(secondPart, secondPartLengths);
  351. }
  352. if (noBreak != null) {
  353. performSpaceResolutionRules2to3(noBreak, noBreakLengths);
  354. }
  355. } else {
  356. if (isFirst) {
  357. removeConditionalBorderAndPadding(secondPart, secondPartLengths, false);
  358. performSpaceResolutionRule1(secondPart, secondPartLengths, false);
  359. }
  360. if (isLast) {
  361. removeConditionalBorderAndPadding(firstPart, firstPartLengths, true);
  362. performSpaceResolutionRule1(firstPart, firstPartLengths, true);
  363. }
  364. if (hasFirstPart()) {
  365. //Now that we've handled isFirst/isLast conditions, we need to look at the
  366. //active part in its normal order so swap it back.
  367. LOG.trace("Swapping first and second parts.");
  368. UnresolvedListElementWithLength[] tempList;
  369. MinOptMax[] tempLengths;
  370. tempList = secondPart;
  371. tempLengths = secondPartLengths;
  372. secondPart = firstPart;
  373. secondPartLengths = firstPartLengths;
  374. firstPart = tempList;
  375. firstPartLengths = tempLengths;
  376. if (hasFirstPart()) {
  377. throw new IllegalStateException("Didn't expect more than one parts in a"
  378. + "no-break condition.");
  379. }
  380. }
  381. performSpaceResolutionRules2to3(secondPart, secondPartLengths);
  382. }
  383. }
  384. private MinOptMax sum(MinOptMax[] lengths) {
  385. MinOptMax sum = MinOptMax.ZERO;
  386. for (MinOptMax length : lengths) {
  387. if (length != null) {
  388. sum = sum.plus(length);
  389. }
  390. }
  391. return sum;
  392. }
  393. private void generate(ListIterator iter) {
  394. MinOptMax spaceBeforeBreak = sum(firstPartLengths);
  395. MinOptMax spaceAfterBreak = sum(secondPartLengths);
  396. boolean hasPrecedingNonBlock = false;
  397. if (breakPoss != null) {
  398. if (spaceBeforeBreak.isNonZero()) {
  399. iter.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false, null, true));
  400. iter.add(new KnuthGlue(spaceBeforeBreak, null, true));
  401. if (breakPoss.isForcedBreak()) {
  402. //Otherwise, the preceding penalty and glue will be cut off
  403. iter.add(new KnuthBox(0, null, true));
  404. }
  405. }
  406. iter.add(new KnuthPenalty(breakPoss.getPenaltyWidth(), breakPoss.getPenaltyValue(),
  407. false, breakPoss.getBreakClass(),
  408. new SpaceHandlingBreakPosition(this, breakPoss), false));
  409. if (breakPoss.getPenaltyValue() <= -KnuthPenalty.INFINITE) {
  410. return; //return early. Not necessary (even wrong) to add additional elements
  411. }
  412. // No break
  413. // TODO: We can't use a MinOptMax for glue2,
  414. // because min <= opt <= max is not always true - why?
  415. MinOptMax noBreakLength = sum(noBreakLengths);
  416. MinOptMax spaceSum = spaceBeforeBreak.plus(spaceAfterBreak);
  417. int glue2width = noBreakLength.getOpt() - spaceSum.getOpt();
  418. int glue2stretch = noBreakLength.getStretch() - spaceSum.getStretch();
  419. int glue2shrink = noBreakLength.getShrink() - spaceSum.getShrink();
  420. if (glue2width != 0 || glue2stretch != 0 || glue2shrink != 0) {
  421. iter.add(new KnuthGlue(glue2width, glue2stretch, glue2shrink, null, true));
  422. }
  423. } else {
  424. if (spaceBeforeBreak.isNonZero()) {
  425. throw new IllegalStateException("spaceBeforeBreak should be 0 in this case");
  426. }
  427. }
  428. Position pos = null;
  429. if (breakPoss == null) {
  430. pos = new SpaceHandlingPosition(this);
  431. }
  432. if (spaceAfterBreak.isNonZero() || pos != null) {
  433. iter.add(new KnuthBox(0, pos, true));
  434. }
  435. if (spaceAfterBreak.isNonZero()) {
  436. iter.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false, null, true));
  437. iter.add(new KnuthGlue(spaceAfterBreak, null, true));
  438. hasPrecedingNonBlock = true;
  439. }
  440. if (isLast && hasPrecedingNonBlock) {
  441. //Otherwise, the preceding penalty and glue will be cut off
  442. iter.add(new KnuthBox(0, null, true));
  443. }
  444. }
  445. /**
  446. * Position class for break possibilities. It is used to notify layout manager about the
  447. * effective spaces and conditional lengths.
  448. */
  449. public static class SpaceHandlingBreakPosition extends Position {
  450. private SpaceResolver resolver;
  451. private Position originalPosition;
  452. /**
  453. * Main constructor.
  454. * @param resolver the space resolver that provides the info about the actual situation
  455. * @param breakPoss the original break possibility that creates this Position
  456. */
  457. public SpaceHandlingBreakPosition(SpaceResolver resolver, BreakElement breakPoss) {
  458. super(null);
  459. this.resolver = resolver;
  460. this.originalPosition = breakPoss.getPosition();
  461. //Unpack since the SpaceHandlingBreakPosition is a non-wrapped Position, too
  462. while (this.originalPosition instanceof NonLeafPosition) {
  463. this.originalPosition = this.originalPosition.getPosition();
  464. }
  465. }
  466. /** @return the space resolver */
  467. public SpaceResolver getSpaceResolver() {
  468. return this.resolver;
  469. }
  470. /**
  471. * Notifies all affected layout managers about the current situation in the part to be
  472. * handled for area generation.
  473. * @param isBreakSituation true if this is a break situation.
  474. * @param side defines to notify about the situation whether before or after the break.
  475. * May be null if isBreakSituation is null.
  476. */
  477. public void notifyBreakSituation(boolean isBreakSituation, RelSide side) {
  478. if (isBreakSituation) {
  479. if (RelSide.BEFORE == side) {
  480. for (int i = 0; i < resolver.secondPart.length; i++) {
  481. resolver.secondPart[i].notifyLayoutManager(resolver.secondPartLengths[i]);
  482. }
  483. } else {
  484. for (int i = 0; i < resolver.firstPart.length; i++) {
  485. resolver.firstPart[i].notifyLayoutManager(resolver.firstPartLengths[i]);
  486. }
  487. }
  488. } else {
  489. for (int i = 0; i < resolver.noBreak.length; i++) {
  490. resolver.noBreak[i].notifyLayoutManager(resolver.noBreakLengths[i]);
  491. }
  492. }
  493. }
  494. /** {@inheritDoc} */
  495. public String toString() {
  496. StringBuffer sb = new StringBuffer();
  497. sb.append("SpaceHandlingBreakPosition(");
  498. sb.append(this.originalPosition);
  499. sb.append(")");
  500. return sb.toString();
  501. }
  502. /**
  503. * @return the original Position instance set at the BreakElement that this Position was
  504. * created for.
  505. */
  506. public Position getOriginalBreakPosition() {
  507. return this.originalPosition;
  508. }
  509. /** {@inheritDoc} */
  510. public Position getPosition() {
  511. return originalPosition;
  512. }
  513. }
  514. /**
  515. * Position class for no-break situations. It is used to notify layout manager about the
  516. * effective spaces and conditional lengths.
  517. */
  518. public static class SpaceHandlingPosition extends Position {
  519. private SpaceResolver resolver;
  520. /**
  521. * Main constructor.
  522. * @param resolver the space resolver that provides the info about the actual situation
  523. */
  524. public SpaceHandlingPosition(SpaceResolver resolver) {
  525. super(null);
  526. this.resolver = resolver;
  527. }
  528. /** @return the space resolver */
  529. public SpaceResolver getSpaceResolver() {
  530. return this.resolver;
  531. }
  532. /**
  533. * Notifies all affected layout managers about the current situation in the part to be
  534. * handled for area generation.
  535. */
  536. public void notifySpaceSituation() {
  537. if (resolver.breakPoss != null) {
  538. throw new IllegalStateException("Only applicable to no-break situations");
  539. }
  540. for (int i = 0; i < resolver.secondPart.length; i++) {
  541. resolver.secondPart[i].notifyLayoutManager(resolver.secondPartLengths[i]);
  542. }
  543. }
  544. /** {@inheritDoc} */
  545. public String toString() {
  546. return "SpaceHandlingPosition";
  547. }
  548. }
  549. /**
  550. * Resolves unresolved elements applying the space resolution rules defined in 4.3.1.
  551. * @param elems the element list
  552. */
  553. public static void resolveElementList(List elems) {
  554. if (LOG.isTraceEnabled()) {
  555. LOG.trace(elems);
  556. }
  557. boolean first = true;
  558. boolean last = false;
  559. boolean skipNextElement = false;
  560. List unresolvedFirst = new java.util.ArrayList();
  561. List unresolvedSecond = new java.util.ArrayList();
  562. List currentGroup;
  563. ListIterator iter = elems.listIterator();
  564. while (iter.hasNext()) {
  565. ListElement el = (ListElement)iter.next();
  566. if (el.isUnresolvedElement()) {
  567. if (LOG.isTraceEnabled()) {
  568. LOG.trace("unresolved found: " + el + " " + first + "/" + last);
  569. }
  570. BreakElement breakPoss = null;
  571. //Clear temp lists
  572. unresolvedFirst.clear();
  573. unresolvedSecond.clear();
  574. //Collect groups
  575. if (el instanceof BreakElement) {
  576. breakPoss = (BreakElement)el;
  577. currentGroup = unresolvedSecond;
  578. } else {
  579. currentGroup = unresolvedFirst;
  580. currentGroup.add(el);
  581. }
  582. iter.remove();
  583. last = true;
  584. skipNextElement = true;
  585. while (iter.hasNext()) {
  586. el = (ListElement)iter.next();
  587. if (el instanceof BreakElement && breakPoss != null) {
  588. skipNextElement = false;
  589. last = false;
  590. break;
  591. } else if (currentGroup == unresolvedFirst && (el instanceof BreakElement)) {
  592. breakPoss = (BreakElement)el;
  593. iter.remove();
  594. currentGroup = unresolvedSecond;
  595. } else if (el.isUnresolvedElement()) {
  596. currentGroup.add(el);
  597. iter.remove();
  598. } else {
  599. last = false;
  600. break;
  601. }
  602. }
  603. //last = !iter.hasNext();
  604. if (breakPoss == null && unresolvedSecond.isEmpty() && !last) {
  605. LOG.trace("Swap first and second parts in no-break condition,"
  606. + " second part is empty.");
  607. //The first list is reversed, so swap if this shouldn't happen
  608. List swapList = unresolvedSecond;
  609. unresolvedSecond = unresolvedFirst;
  610. unresolvedFirst = swapList;
  611. }
  612. LOG.debug("----start space resolution (first=" + first + ", last=" + last + ")...");
  613. SpaceResolver resolver = new SpaceResolver(
  614. unresolvedFirst, breakPoss, unresolvedSecond, first, last);
  615. if (!last) {
  616. iter.previous();
  617. }
  618. resolver.generate(iter);
  619. if (!last && skipNextElement) {
  620. iter.next();
  621. }
  622. LOG.debug("----end space resolution.");
  623. }
  624. first = false;
  625. }
  626. }
  627. /**
  628. * Inspects an effective element list and notifies all layout managers about the state of
  629. * the spaces and conditional lengths.
  630. * @param effectiveList the effective element list
  631. * @param startElementIndex index of the first element in the part to be processed
  632. * @param endElementIndex index of the last element in the part to be processed
  633. * @param prevBreak index of the the break possibility just before this part (used to
  634. * identify a break condition, lastBreak &lt;= 0 represents a no-break condition)
  635. */
  636. public static void performConditionalsNotification(List effectiveList,
  637. int startElementIndex, int endElementIndex, int prevBreak) {
  638. KnuthElement el = null;
  639. if (prevBreak > 0) {
  640. el = (KnuthElement)effectiveList.get(prevBreak);
  641. }
  642. SpaceResolver.SpaceHandlingBreakPosition beforeBreak = null;
  643. SpaceResolver.SpaceHandlingBreakPosition afterBreak = null;
  644. if (el != null && el.isPenalty()) {
  645. Position pos = el.getPosition();
  646. if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) {
  647. beforeBreak = (SpaceResolver.SpaceHandlingBreakPosition)pos;
  648. beforeBreak.notifyBreakSituation(true, RelSide.BEFORE);
  649. }
  650. }
  651. el = endElementIndex > -1 ? (KnuthElement) effectiveList.get(endElementIndex) : null;
  652. if (el != null && el.isPenalty()) {
  653. Position pos = el.getPosition();
  654. if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) {
  655. afterBreak = (SpaceResolver.SpaceHandlingBreakPosition)pos;
  656. afterBreak.notifyBreakSituation(true, RelSide.AFTER);
  657. }
  658. }
  659. for (int i = startElementIndex; i <= endElementIndex; i++) {
  660. Position pos = ((KnuthElement)effectiveList.get(i)).getPosition();
  661. if (pos instanceof SpaceResolver.SpaceHandlingPosition) {
  662. ((SpaceResolver.SpaceHandlingPosition)pos).notifySpaceSituation();
  663. } else if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) {
  664. SpaceResolver.SpaceHandlingBreakPosition noBreak;
  665. noBreak = (SpaceResolver.SpaceHandlingBreakPosition)pos;
  666. if (noBreak != beforeBreak && noBreak != afterBreak) {
  667. noBreak.notifyBreakSituation(false, null);
  668. }
  669. }
  670. }
  671. }
  672. }