001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /* $Id: SpaceResolver.java 521185 2007-03-22 10:13:25Z vhennebert $ */
019:
020: package org.apache.fop.layoutmgr;
021:
022: import java.util.LinkedList;
023: import java.util.List;
024: import java.util.ListIterator;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028: import org.apache.fop.traits.MinOptMax;
029:
030: /**
031: * This class resolves spaces and conditional borders and paddings by replacing the
032: * UnresolvedListElements descendants by the right combination of KnuthElements on an element
033: * list.
034: */
035: public class SpaceResolver {
036:
037: /** Logger instance */
038: protected static Log log = LogFactory.getLog(SpaceResolver.class);
039:
040: private UnresolvedListElementWithLength[] firstPart;
041: private BreakElement breakPoss;
042: private UnresolvedListElementWithLength[] secondPart;
043: private UnresolvedListElementWithLength[] noBreak;
044:
045: private MinOptMax[] firstPartLengths;
046: private MinOptMax[] secondPartLengths;
047: private MinOptMax[] noBreakLengths;
048:
049: private boolean isFirst;
050: private boolean isLast;
051:
052: /**
053: * Main constructor.
054: * @param first Element list before a break (optional)
055: * @param breakPoss Break possibility (optional)
056: * @param second Element list after a break (or if no break possibility in vicinity)
057: * @param isFirst Resolution at the beginning of a (full) element list
058: * @param isLast Resolution at the end of a (full) element list
059: */
060: private SpaceResolver(List first, BreakElement breakPoss,
061: List second, boolean isFirst, boolean isLast) {
062: this .isFirst = isFirst;
063: this .isLast = isLast;
064: //Create combined no-break list
065: int c = 0;
066: if (first != null) {
067: c += first.size();
068: }
069: if (second != null) {
070: c += second.size();
071: }
072: noBreak = new UnresolvedListElementWithLength[c];
073: noBreakLengths = new MinOptMax[c];
074: int i = 0;
075: ListIterator iter;
076: if (first != null) {
077: iter = first.listIterator();
078: while (iter.hasNext()) {
079: noBreak[i] = (UnresolvedListElementWithLength) iter
080: .next();
081: noBreakLengths[i] = noBreak[i].getLength();
082: i++;
083: }
084: }
085: if (second != null) {
086: iter = second.listIterator();
087: while (iter.hasNext()) {
088: noBreak[i] = (UnresolvedListElementWithLength) iter
089: .next();
090: noBreakLengths[i] = noBreak[i].getLength();
091: i++;
092: }
093: }
094:
095: //Add pending elements from higher level FOs
096: if (breakPoss != null) {
097: if (breakPoss.getPendingAfterMarks() != null) {
098: if (log.isTraceEnabled()) {
099: log.trace(" adding pending before break: "
100: + breakPoss.getPendingAfterMarks());
101: }
102: first.addAll(0, breakPoss.getPendingAfterMarks());
103: }
104: if (breakPoss.getPendingBeforeMarks() != null) {
105: if (log.isTraceEnabled()) {
106: log.trace(" adding pending after break: "
107: + breakPoss.getPendingBeforeMarks());
108: }
109: second.addAll(0, breakPoss.getPendingBeforeMarks());
110: }
111: }
112: if (log.isTraceEnabled()) {
113: log.trace("before: " + first);
114: log.trace(" break: " + breakPoss);
115: log.trace("after: " + second);
116: log.trace("NO-BREAK: " + toString(noBreak, noBreakLengths));
117: }
118:
119: if (first != null) {
120: firstPart = new UnresolvedListElementWithLength[first
121: .size()];
122: firstPartLengths = new MinOptMax[firstPart.length];
123: first.toArray(firstPart);
124: for (i = 0; i < firstPart.length; i++) {
125: firstPartLengths[i] = firstPart[i].getLength();
126: }
127: }
128: this .breakPoss = breakPoss;
129: if (second != null) {
130: secondPart = new UnresolvedListElementWithLength[second
131: .size()];
132: secondPartLengths = new MinOptMax[secondPart.length];
133: second.toArray(secondPart);
134: for (i = 0; i < secondPart.length; i++) {
135: secondPartLengths[i] = secondPart[i].getLength();
136: }
137: }
138: resolve();
139: }
140:
141: private String toString(Object[] arr1, Object[] arr2) {
142: if (arr1.length != arr2.length) {
143: new IllegalArgumentException(
144: "The length of both arrays must be equal");
145: }
146: StringBuffer sb = new StringBuffer("[");
147: for (int i = 0; i < arr1.length; i++) {
148: if (i > 0) {
149: sb.append(", ");
150: }
151: sb.append(String.valueOf(arr1[i]));
152: sb.append("/");
153: sb.append(String.valueOf(arr2[i]));
154: }
155: sb.append("]");
156: return sb.toString();
157: }
158:
159: private void removeConditionalBorderAndPadding(
160: UnresolvedListElement[] elems, MinOptMax[] lengths,
161: boolean reverse) {
162: for (int i = 0; i < elems.length; i++) {
163: int effIndex;
164: if (reverse) {
165: effIndex = elems.length - 1 - i;
166: } else {
167: effIndex = i;
168: }
169: if (elems[effIndex] instanceof BorderOrPaddingElement) {
170: BorderOrPaddingElement bop = (BorderOrPaddingElement) elems[effIndex];
171: if (bop.isConditional()
172: && !(bop.isFirst() || bop.isLast())) {
173: if (log.isDebugEnabled()) {
174: log
175: .debug("Nulling conditional element: "
176: + bop);
177: }
178: lengths[effIndex] = null;
179: }
180: }
181: }
182: if (log.isTraceEnabled() && elems.length > 0) {
183: log.trace("-->Resulting list: " + toString(elems, lengths));
184: }
185: }
186:
187: private void performSpaceResolutionRule1(
188: UnresolvedListElement[] elems, MinOptMax[] lengths,
189: boolean reverse) {
190: for (int i = 0; i < elems.length; i++) {
191: int effIndex;
192: if (reverse) {
193: effIndex = elems.length - 1 - i;
194: } else {
195: effIndex = i;
196: }
197: if (lengths[effIndex] == null) {
198: //Zeroed border or padding doesn't create a fence
199: continue;
200: } else if (elems[effIndex] instanceof BorderOrPaddingElement) {
201: //Border or padding form fences!
202: break;
203: } else if (!elems[effIndex].isConditional()) {
204: break;
205: }
206: if (log.isDebugEnabled()) {
207: log
208: .debug("Nulling conditional element using 4.3.1, rule 1: "
209: + elems[effIndex]);
210: }
211: lengths[effIndex] = null;
212: }
213: if (log.isTraceEnabled() && elems.length > 0) {
214: log.trace("-->Resulting list: " + toString(elems, lengths));
215: }
216: }
217:
218: private void performSpaceResolutionRules2to3(
219: UnresolvedListElement[] elems, MinOptMax[] lengths,
220: int start, int end) {
221: if (log.isTraceEnabled()) {
222: log.trace("rule 2-3: " + start + "-" + end);
223: }
224: SpaceElement space;
225: int remaining;
226:
227: //Rule 2 (4.3.1, XSL 1.0)
228: boolean hasForcing = false;
229: remaining = 0;
230: for (int i = start; i <= end; i++) {
231: if (lengths[i] == null) {
232: continue;
233: }
234: remaining++;
235: space = (SpaceElement) elems[i];
236: if (space.isForcing()) {
237: hasForcing = true;
238: break;
239: }
240: }
241: if (remaining == 0) {
242: return; //shortcut
243: }
244: if (hasForcing) {
245: for (int i = start; i <= end; i++) {
246: if (lengths[i] == null) {
247: continue;
248: }
249: space = (SpaceElement) elems[i];
250: if (!space.isForcing()) {
251: if (log.isDebugEnabled()) {
252: log
253: .debug("Nulling non-forcing space-specifier using 4.3.1, rule 2: "
254: + elems[i]);
255: }
256: lengths[i] = null;
257: }
258: }
259: return; //If rule is triggered skip rule 3
260: }
261:
262: //Rule 3 (4.3.1, XSL 1.0)
263: //Determine highes precedence
264: int highestPrecedence = Integer.MIN_VALUE;
265: for (int i = start; i <= end; i++) {
266: if (lengths[i] == null) {
267: continue;
268: }
269: space = (SpaceElement) elems[i];
270: highestPrecedence = Math.max(highestPrecedence, space
271: .getPrecedence());
272: }
273: if (highestPrecedence != 0 && log.isDebugEnabled()) {
274: log.debug("Highest precedence is " + highestPrecedence);
275: }
276: //Suppress space-specifiers with lower precedence
277: remaining = 0;
278: int greatestOptimum = Integer.MIN_VALUE;
279: for (int i = start; i <= end; i++) {
280: if (lengths[i] == null) {
281: continue;
282: }
283: space = (SpaceElement) elems[i];
284: if (space.getPrecedence() != highestPrecedence) {
285: if (log.isDebugEnabled()) {
286: log
287: .debug("Nulling space-specifier with precedence "
288: + space.getPrecedence()
289: + " using 4.3.1, rule 3: "
290: + elems[i]);
291: }
292: lengths[i] = null;
293: } else {
294: greatestOptimum = Math.max(greatestOptimum, space
295: .getLength().opt);
296: remaining++;
297: }
298: }
299: if (log.isDebugEnabled()) {
300: log.debug("Greatest optimum: " + greatestOptimum);
301: }
302: if (remaining <= 1) {
303: return;
304: }
305: //Suppress space-specifiers with smaller optimum length
306: remaining = 0;
307: for (int i = start; i <= end; i++) {
308: if (lengths[i] == null) {
309: continue;
310: }
311: space = (SpaceElement) elems[i];
312: if (space.getLength().opt < greatestOptimum) {
313: if (log.isDebugEnabled()) {
314: log
315: .debug("Nulling space-specifier with smaller optimum length "
316: + "using 4.3.1, rule 3: "
317: + elems[i]);
318: }
319: lengths[i] = null;
320: } else {
321: remaining++;
322: }
323: }
324: if (remaining <= 1) {
325: return;
326: }
327: //Construct resolved space-specifier from the remaining spaces
328: int min = Integer.MIN_VALUE;
329: int max = Integer.MAX_VALUE;
330: for (int i = start; i <= end; i++) {
331: if (lengths[i] == null) {
332: continue;
333: }
334: space = (SpaceElement) elems[i];
335: min = Math.max(min, space.getLength().min);
336: max = Math.min(max, space.getLength().max);
337: if (remaining > 1) {
338: if (log.isDebugEnabled()) {
339: log
340: .debug("Nulling non-last space-specifier using 4.3.1, rule 3, second part: "
341: + elems[i]);
342: }
343: lengths[i] = null;
344: remaining--;
345: } else {
346: lengths[i].min = min;
347: lengths[i].max = max;
348: }
349: }
350:
351: if (log.isTraceEnabled() && elems.length > 0) {
352: log.trace("Remaining spaces: " + remaining);
353: log.trace("-->Resulting list: " + toString(elems, lengths));
354: }
355: }
356:
357: private void performSpaceResolutionRules2to3(
358: UnresolvedListElement[] elems, MinOptMax[] lengths) {
359: int start = 0;
360: int i = start;
361: while (i < elems.length) {
362: if (elems[i] instanceof SpaceElement) {
363: while (i < elems.length) {
364: if (elems[i] == null
365: || elems[i] instanceof SpaceElement) {
366: i++;
367: } else {
368: break;
369: }
370: }
371: performSpaceResolutionRules2to3(elems, lengths, start,
372: i - 1);
373: }
374: i++;
375: start = i;
376: }
377: }
378:
379: private boolean hasFirstPart() {
380: return firstPart != null && firstPart.length > 0;
381: }
382:
383: private boolean hasSecondPart() {
384: return secondPart != null && secondPart.length > 0;
385: }
386:
387: private void resolve() {
388: if (breakPoss != null) {
389: if (hasFirstPart()) {
390: removeConditionalBorderAndPadding(firstPart,
391: firstPartLengths, true);
392: performSpaceResolutionRule1(firstPart,
393: firstPartLengths, true);
394: performSpaceResolutionRules2to3(firstPart,
395: firstPartLengths);
396: }
397: if (hasSecondPart()) {
398: removeConditionalBorderAndPadding(secondPart,
399: secondPartLengths, false);
400: performSpaceResolutionRule1(secondPart,
401: secondPartLengths, false);
402: performSpaceResolutionRules2to3(secondPart,
403: secondPartLengths);
404: }
405: if (noBreak != null) {
406: performSpaceResolutionRules2to3(noBreak, noBreakLengths);
407: }
408: } else {
409: if (isFirst) {
410: removeConditionalBorderAndPadding(secondPart,
411: secondPartLengths, false);
412: performSpaceResolutionRule1(secondPart,
413: secondPartLengths, false);
414: }
415: if (isLast) {
416: removeConditionalBorderAndPadding(firstPart,
417: firstPartLengths, true);
418: performSpaceResolutionRule1(firstPart,
419: firstPartLengths, true);
420: }
421:
422: if (hasFirstPart()) {
423: //Now that we've handled isFirst/isLast conditions, we need to look at the
424: //active part in its normal order so swap it back.
425: log.trace("Swapping first and second parts.");
426: UnresolvedListElementWithLength[] tempList;
427: MinOptMax[] tempLengths;
428: tempList = secondPart;
429: tempLengths = secondPartLengths;
430: secondPart = firstPart;
431: secondPartLengths = firstPartLengths;
432: firstPart = tempList;
433: firstPartLengths = tempLengths;
434: if (hasFirstPart()) {
435: throw new IllegalStateException(
436: "Didn't expect more than one parts in a"
437: + "no-break condition.");
438: }
439: }
440: performSpaceResolutionRules2to3(secondPart,
441: secondPartLengths);
442: }
443: }
444:
445: private MinOptMax sum(MinOptMax[] lengths) {
446: MinOptMax sum = new MinOptMax();
447: for (int i = 0; i < lengths.length; i++) {
448: if (lengths[i] != null) {
449: sum.add(lengths[i]);
450: }
451: }
452: return sum;
453: }
454:
455: private void generate(ListIterator iter) {
456: MinOptMax noBreakLength = new MinOptMax();
457: MinOptMax glue1; //space before break possibility if break occurs
458: //MinOptMax glue2; //difference between glue 1 and 3 when no break occurs
459: MinOptMax glue3; //space after break possibility if break occurs
460: glue1 = sum(firstPartLengths);
461: glue3 = sum(secondPartLengths);
462: noBreakLength = sum(noBreakLengths);
463:
464: //This doesn't produce the right glue2
465: //glue2 = new MinOptMax(noBreakLength);
466: //glue2.subtract(glue1);
467: //glue2.subtract(glue3);
468:
469: int glue2w = noBreakLength.opt - glue1.opt - glue3.opt;
470: int glue2stretch = (noBreakLength.max - noBreakLength.opt);
471: int glue2shrink = (noBreakLength.opt - noBreakLength.min);
472: glue2stretch -= glue1.max - glue1.opt;
473: glue2stretch -= glue3.max - glue3.opt;
474: glue2shrink -= glue1.opt - glue1.min;
475: glue2shrink -= glue3.opt - glue3.min;
476:
477: boolean hasPrecedingNonBlock = false;
478: boolean forcedBreak = false;
479: if (log.isDebugEnabled()) {
480: log.debug("noBreakLength=" + noBreakLength + ", glue1="
481: + glue1 + ", glue2=" + glue2w + "+" + glue2stretch
482: + "-" + glue2shrink + ", glue3=" + glue3);
483: }
484: if (breakPoss != null) {
485: if (glue1.isNonZero()) {
486: iter.add(new KnuthPenalty(0, KnuthPenalty.INFINITE,
487: false, (Position) null, true));
488: iter.add(new KnuthGlue(glue1.opt,
489: glue1.max - glue1.opt, glue1.opt - glue1.min,
490: (Position) null, true));
491: }
492: iter.add(new KnuthPenalty(breakPoss.getPenaltyWidth(),
493: breakPoss.getPenaltyValue(), false, breakPoss
494: .getBreakClass(),
495: new SpaceHandlingBreakPosition(this , breakPoss),
496: false));
497: if (breakPoss.getPenaltyValue() <= -KnuthPenalty.INFINITE) {
498: return; //return early. Not necessary (even wrong) to add additional elements
499: }
500: if (glue2w != 0 || glue2stretch != 0 || glue2shrink != 0) {
501: iter.add(new KnuthGlue(glue2w, glue2stretch,
502: glue2shrink, (Position) null, true));
503: }
504: } else {
505: if (glue1.isNonZero()) {
506: throw new IllegalStateException(
507: "glue1 should be 0 in this case");
508: }
509: }
510: Position pos = null;
511: if (breakPoss == null) {
512: pos = new SpaceHandlingPosition(this );
513: }
514: if (glue3.isNonZero() || pos != null) {
515: iter.add(new KnuthBox(0, pos, true));
516: }
517: if (glue3.isNonZero()) {
518: iter.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false,
519: (Position) null, true));
520: iter.add(new KnuthGlue(glue3.opt, glue3.max - glue3.opt,
521: glue3.opt - glue3.min, (Position) null, true));
522: hasPrecedingNonBlock = true;
523: }
524: if (isLast && hasPrecedingNonBlock) {
525: //Otherwise, the preceding penalty and glue will be cut off
526: iter.add(new KnuthBox(0, (Position) null, true));
527: }
528: }
529:
530: /**
531: * Position class for break possibilities. It is used to notify layout manager about the
532: * effective spaces and conditional lengths.
533: */
534: public static class SpaceHandlingBreakPosition extends Position {
535:
536: private SpaceResolver resolver;
537: private Position originalPosition;
538:
539: /**
540: * Main constructor.
541: * @param resolver the space resolver that provides the info about the actual situation
542: * @param breakPoss the original break possibility that creates this Position
543: */
544: public SpaceHandlingBreakPosition(SpaceResolver resolver,
545: BreakElement breakPoss) {
546: super (null);
547: this .resolver = resolver;
548: this .originalPosition = breakPoss.getPosition();
549: //Unpack since the SpaceHandlingBreakPosition is a non-wrapped Position, too
550: while (this .originalPosition instanceof NonLeafPosition) {
551: this .originalPosition = this .originalPosition
552: .getPosition();
553: }
554: }
555:
556: /** @return the space resolver */
557: public SpaceResolver getSpaceResolver() {
558: return this .resolver;
559: }
560:
561: /**
562: * Notifies all affected layout managers about the current situation in the part to be
563: * handled for area generation.
564: * @param isBreakSituation true if this is a break situation.
565: * @param side defines to notify about the situation whether before or after the break.
566: * May be null if isBreakSituation is null.
567: */
568: public void notifyBreakSituation(boolean isBreakSituation,
569: RelSide side) {
570: if (isBreakSituation) {
571: if (RelSide.BEFORE == side) {
572: for (int i = 0; i < resolver.secondPart.length; i++) {
573: resolver.secondPart[i]
574: .notifyLayoutManager(resolver.secondPartLengths[i]);
575: }
576: } else {
577: for (int i = 0; i < resolver.firstPart.length; i++) {
578: resolver.firstPart[i]
579: .notifyLayoutManager(resolver.firstPartLengths[i]);
580: }
581: }
582: } else {
583: for (int i = 0; i < resolver.noBreak.length; i++) {
584: resolver.noBreak[i]
585: .notifyLayoutManager(resolver.noBreakLengths[i]);
586: }
587: }
588: }
589:
590: /** @see java.lang.Object#toString() */
591: public String toString() {
592: StringBuffer sb = new StringBuffer();
593: sb.append("SpaceHandlingBreakPosition(");
594: sb.append(this .originalPosition);
595: sb.append(")");
596: return sb.toString();
597: }
598:
599: /**
600: * @return the original Position instance set at the BreakElement that this Position was
601: * created for.
602: */
603: public Position getOriginalBreakPosition() {
604: return this .originalPosition;
605: }
606: }
607:
608: /**
609: * Position class for no-break situations. It is used to notify layout manager about the
610: * effective spaces and conditional lengths.
611: */
612: public static class SpaceHandlingPosition extends Position {
613:
614: private SpaceResolver resolver;
615:
616: /**
617: * Main constructor.
618: * @param resolver the space resolver that provides the info about the actual situation
619: */
620: public SpaceHandlingPosition(SpaceResolver resolver) {
621: super (null);
622: this .resolver = resolver;
623: }
624:
625: /** @return the space resolver */
626: public SpaceResolver getSpaceResolver() {
627: return this .resolver;
628: }
629:
630: /**
631: * Notifies all affected layout managers about the current situation in the part to be
632: * handled for area generation.
633: */
634: public void notifySpaceSituation() {
635: if (resolver.breakPoss != null) {
636: throw new IllegalStateException(
637: "Only applicable to no-break situations");
638: }
639: for (int i = 0; i < resolver.secondPart.length; i++) {
640: resolver.secondPart[i]
641: .notifyLayoutManager(resolver.secondPartLengths[i]);
642: }
643: }
644:
645: /** @see java.lang.Object#toString() */
646: public String toString() {
647: StringBuffer sb = new StringBuffer();
648: sb.append("SpaceHandlingPosition");
649: return sb.toString();
650: }
651: }
652:
653: /**
654: * Resolves unresolved elements applying the space resolution rules defined in 4.3.1.
655: * @param elems the element list
656: */
657: public static void resolveElementList(LinkedList elems) {
658: if (log.isTraceEnabled()) {
659: log.trace(elems);
660: }
661: boolean first = true;
662: boolean last = false;
663: boolean skipNextElement = false;
664: List unresolvedFirst = new java.util.ArrayList();
665: List unresolvedSecond = new java.util.ArrayList();
666: List currentGroup;
667: ListIterator iter = elems.listIterator();
668: while (iter.hasNext()) {
669: ListElement el = (ListElement) iter.next();
670: if (el.isUnresolvedElement()) {
671: if (log.isTraceEnabled()) {
672: log.trace("unresolved found: " + el + " " + first
673: + "/" + last);
674: }
675: BreakElement breakPoss = null;
676: //Clear temp lists
677: unresolvedFirst.clear();
678: unresolvedSecond.clear();
679: //Collect groups
680: if (el instanceof BreakElement) {
681: breakPoss = (BreakElement) el;
682: currentGroup = unresolvedSecond;
683: } else {
684: currentGroup = unresolvedFirst;
685: currentGroup.add(el);
686: }
687: iter.remove();
688: last = true;
689: skipNextElement = true;
690: while (iter.hasNext()) {
691: el = (ListElement) iter.next();
692: if (el instanceof BreakElement && breakPoss != null) {
693: skipNextElement = false;
694: last = false;
695: break;
696: } else if (currentGroup == unresolvedFirst
697: && (el instanceof BreakElement)) {
698: breakPoss = (BreakElement) el;
699: iter.remove();
700: currentGroup = unresolvedSecond;
701: } else if (el.isUnresolvedElement()) {
702: currentGroup.add(el);
703: iter.remove();
704: } else {
705: last = false;
706: break;
707: }
708: }
709: //last = !iter.hasNext();
710: if (breakPoss == null && unresolvedSecond.size() == 0
711: && !last) {
712: log
713: .trace("Swap first and second parts in no-break condition,"
714: + " second part is empty.");
715: //The first list is reversed, so swap if this shouldn't happen
716: List swapList = unresolvedSecond;
717: unresolvedSecond = unresolvedFirst;
718: unresolvedFirst = swapList;
719: }
720:
721: log.debug("----start space resolution (first=" + first
722: + ", last=" + last + ")...");
723: SpaceResolver resolver = new SpaceResolver(
724: unresolvedFirst, breakPoss, unresolvedSecond,
725: first, last);
726: if (!last) {
727: iter.previous();
728: }
729: resolver.generate(iter);
730: if (!last && skipNextElement) {
731: iter.next();
732: }
733: log.debug("----end space resolution.");
734: }
735: first = false;
736: }
737: }
738:
739: /**
740: * Inspects an effective element list and notifies all layout managers about the state of
741: * the spaces and conditional lengths.
742: * @param effectiveList the effective element list
743: * @param startElementIndex index of the first element in the part to be processed
744: * @param endElementIndex index of the last element in the part to be processed
745: * @param prevBreak index of the the break possibility just before this part (used to
746: * identify a break condition, lastBreak <= 0 represents a no-break condition)
747: */
748: public static void performConditionalsNotification(
749: List effectiveList, int startElementIndex,
750: int endElementIndex, int prevBreak) {
751: KnuthElement el = null;
752: if (prevBreak > 0) {
753: el = (KnuthElement) effectiveList.get(prevBreak);
754: }
755: SpaceResolver.SpaceHandlingBreakPosition beforeBreak = null;
756: SpaceResolver.SpaceHandlingBreakPosition afterBreak = null;
757: if (el != null && el.isPenalty()) {
758: Position pos = el.getPosition();
759: if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) {
760: beforeBreak = (SpaceResolver.SpaceHandlingBreakPosition) pos;
761: beforeBreak.notifyBreakSituation(true, RelSide.BEFORE);
762: }
763: }
764: el = (KnuthElement) effectiveList.get(endElementIndex);
765: if (el != null && el.isPenalty()) {
766: Position pos = el.getPosition();
767: if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) {
768: afterBreak = (SpaceResolver.SpaceHandlingBreakPosition) pos;
769: afterBreak.notifyBreakSituation(true, RelSide.AFTER);
770: }
771: }
772: for (int i = startElementIndex; i <= endElementIndex; i++) {
773: Position pos = ((KnuthElement) effectiveList.get(i))
774: .getPosition();
775: if (pos instanceof SpaceResolver.SpaceHandlingPosition) {
776: ((SpaceResolver.SpaceHandlingPosition) pos)
777: .notifySpaceSituation();
778: } else if (pos instanceof SpaceResolver.SpaceHandlingBreakPosition) {
779: SpaceResolver.SpaceHandlingBreakPosition noBreak;
780: noBreak = (SpaceResolver.SpaceHandlingBreakPosition) pos;
781: if (noBreak != beforeBreak && noBreak != afterBreak) {
782: noBreak.notifyBreakSituation(false, null);
783: }
784: }
785: }
786: }
787:
788: }
|