KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > layoutmgr > BlockStackingLayoutManager


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: BlockStackingLayoutManager.java 478928 2006-11-24 17:32:48Z vhennebert $ */
19
20 package org.apache.fop.layoutmgr;
21
22 import java.util.Iterator JavaDoc;
23 import java.util.LinkedList JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.ListIterator JavaDoc;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.fop.area.Area;
30 import org.apache.fop.area.BlockParent;
31 import org.apache.fop.area.Block;
32 import org.apache.fop.fo.FObj;
33 import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
34 import org.apache.fop.fo.properties.SpaceProperty;
35 import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
36 import org.apache.fop.layoutmgr.inline.LineLayoutManager;
37 import org.apache.fop.traits.MinOptMax;
38
39 /**
40  * Base LayoutManager class for all areas which stack their child
41  * areas in the block-progression direction, such as Flow, Block, ListBlock.
42  */

43 public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
44                                                  implements BlockLevelLayoutManager {
45
46     /**
47      * logging instance
48      */

49     private static Log log = LogFactory.getLog(BlockStackingLayoutManager.class);
50
51     /**
52      * Reference to FO whose areas it's managing or to the traits
53      * of the FO.
54      */

55     //protected LayoutManager curChildLM = null; AbstractLayoutManager also defines this!
56
protected BlockParent parentArea = null;
57
58     /** Value of the block-progression-unit (non-standard property) */
59     protected int bpUnit = 0;
60     /** space-before value adjusted for block-progression-unit handling */
61     protected int adjustedSpaceBefore = 0;
62     /** space-after value adjusted for block-progression-unit handling */
63     protected int adjustedSpaceAfter = 0;
64     /** Only used to store the original list when createUnitElements is called */
65     protected LinkedList JavaDoc storedList = null;
66     /** Indicates whether break before has been served or not */
67     protected boolean breakBeforeServed = false;
68     /** Indicates whether the first visible mark has been returned by this LM, yet */
69     protected boolean firstVisibleMarkServed = false;
70     /** Reference IPD available */
71     protected int referenceIPD = 0;
72     /** the effective start-indent value */
73     protected int startIndent = 0;
74     /** the effective end-indent value */
75     protected int endIndent = 0;
76     /**
77      * Holds the (one-time use) fo:block space-before
78      * and -after properties. Large fo:blocks are split
79      * into multiple Area. Blocks to accomodate the subsequent
80      * regions (pages) they are placed on. space-before
81      * is applied at the beginning of the first
82      * Block and space-after at the end of the last Block
83      * used in rendering the fo:block.
84      */

85     protected MinOptMax foSpaceBefore = null;
86     /** see foSpaceBefore */
87     protected MinOptMax foSpaceAfter = null;
88
89     private Position auxiliaryPosition;
90
91     private int contentAreaIPD = 0;
92     
93     /**
94      * @param node the fo this LM deals with
95      */

96     public BlockStackingLayoutManager(FObj node) {
97         super(node);
98         setGeneratesBlockArea(true);
99     }
100
101     /**
102      * @return current area being filled
103      */

104     protected BlockParent getCurrentArea() {
105         return this.parentArea;
106     }
107
108
109     /**
110      * Set the current area being filled.
111      * @param parentArea the current area to be filled
112      */

113     protected void setCurrentArea(BlockParent parentArea) {
114         this.parentArea = parentArea;
115     }
116
117     /**
118      * Add a block spacer for space before and space after a block.
119      * This adds an empty Block area that acts as a block space.
120      *
121      * @param adjust the adjustment value
122      * @param minoptmax the min/opt/max value of the spacing
123      */

124     public void addBlockSpacing(double adjust, MinOptMax minoptmax) {
125         int sp = TraitSetter.getEffectiveSpace(adjust, minoptmax);
126         if (sp != 0) {
127             Block spacer = new Block();
128             spacer.setBPD(sp);
129             parentLM.addChildArea(spacer);
130         }
131     }
132
133     /**
134      * Add the childArea to the passed area.
135      * Called by child LayoutManager when it has filled one of its areas.
136      * The LM should already have an Area in which to put the child.
137      * See if the area will fit in the current area.
138      * If so, add it. Otherwise initiate breaking.
139      * @param childArea the area to add: will be some block-stacked Area.
140      * @param parentArea the area in which to add the childArea
141      */

142     protected void addChildToArea(Area childArea,
143                                      BlockParent parentArea) {
144         // This should be a block-level Area (Block in the generic sense)
145
if (!(childArea instanceof Block)) {
146             //log.error("Child not a Block in BlockStackingLM!");
147
}
148
149         parentArea.addBlock((Block) childArea);
150         flush(); // hand off current area to parent
151
}
152
153
154     /**
155      * Add the childArea to the current area.
156      * Called by child LayoutManager when it has filled one of its areas.
157      * The LM should already have an Area in which to put the child.
158      * See if the area will fit in the current area.
159      * If so, add it. Otherwise initiate breaking.
160      * @param childArea the area to add: will be some block-stacked Area.
161      */

162     public void addChildArea(Area childArea) {
163         addChildToArea(childArea, getCurrentArea());
164     }
165
166     /**
167      * Force current area to be added to parent area.
168      */

169     protected void flush() {
170         if (getCurrentArea() != null) {
171             parentLM.addChildArea(getCurrentArea());
172         }
173     }
174
175     /** @return a cached auxiliary Position instance used for things like spaces. */
176     protected Position getAuxiliaryPosition() {
177         if (this.auxiliaryPosition == null) {
178             this.auxiliaryPosition = new NonLeafPosition(this, null);
179         }
180         return this.auxiliaryPosition;
181     }
182     
183     /**
184      * @param len length in millipoints to span with bp units
185      * @return the minimum integer n such that n * bpUnit >= len
186      */

187     protected int neededUnits(int len) {
188         return (int) Math.ceil((float)len / bpUnit);
189     }
190
191     /**
192      * Determines and sets the content area IPD based on available reference area IPD, start- and
193      * end-indent properties.
194      * end-indent is adjusted based on overconstrained geometry rules, if necessary.
195      * @return the resulting content area IPD
196      */

197     protected int updateContentAreaIPDwithOverconstrainedAdjust() {
198         int ipd = referenceIPD - (startIndent + endIndent);
199         if (ipd < 0) {
200             //5.3.4, XSL 1.0, Overconstrained Geometry
201
log.debug("Adjusting end-indent based on overconstrained geometry rules for " + fobj);
202             endIndent += ipd;
203             ipd = 0;
204             //TODO Should we skip layout for a block that has ipd=0?
205
}
206         setContentAreaIPD(ipd);
207         return ipd;
208     }
209     
210     /**
211      * Sets the content area IPD by directly supplying the value.
212      * end-indent is adjusted based on overconstrained geometry rules, if necessary.
213      * @return the resulting content area IPD
214      */

215     protected int updateContentAreaIPDwithOverconstrainedAdjust(int contentIPD) {
216         int ipd = referenceIPD - (contentIPD + (startIndent + endIndent));
217         if (ipd < 0) {
218             //5.3.4, XSL 1.0, Overconstrained Geometry
219
log.debug("Adjusting end-indent based on overconstrained geometry rules for " + fobj);
220             endIndent += ipd;
221         }
222         setContentAreaIPD(contentIPD);
223         return contentIPD;
224     }
225     
226     /**
227      * @see LayoutManager#getNextKnuthElements(LayoutContext, int)
228      */

229     public LinkedList JavaDoc getNextKnuthElements(LayoutContext context, int alignment) {
230         //log.debug("BLM.getNextKnuthElements> keep-together = "
231
// + layoutProps.keepTogether.getType());
232
//log.debug(" keep-with-previous = " +
233
// layoutProps.keepWithPrevious.getType());
234
//log.debug(" keep-with-next = " +
235
// layoutProps.keepWithNext.getType());
236
BlockLevelLayoutManager curLM; // currently active LM
237
BlockLevelLayoutManager prevLM = null; // previously active LM
238

239         referenceIPD = context.getRefIPD();
240         
241         updateContentAreaIPDwithOverconstrainedAdjust();
242
243         LinkedList JavaDoc returnedList = null;
244         LinkedList JavaDoc contentList = new LinkedList JavaDoc();
245         LinkedList JavaDoc returnList = new LinkedList JavaDoc();
246
247         if (!breakBeforeServed) {
248             try {
249                 if (addKnuthElementsForBreakBefore(returnList, context)) {
250                     return returnList;
251                 }
252             } finally {
253                 breakBeforeServed = true;
254             }
255         }
256
257         if (!firstVisibleMarkServed) {
258             addKnuthElementsForSpaceBefore(returnList, alignment);
259         }
260         
261         addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
262         firstVisibleMarkServed = true;
263
264         //Spaces, border and padding to be repeated at each break
265
addPendingMarks(context);
266         
267         while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
268             LayoutContext childLC = new LayoutContext(0);
269             childLC.copyPendingMarksFrom(context);
270             if (curLM instanceof LineLayoutManager) {
271                 // curLM is a LineLayoutManager
272
// set stackLimit for lines (stack limit is now i-p-direction, not b-p-direction!)
273
childLC.setStackLimit(new MinOptMax(getContentAreaIPD()));
274                 childLC.setRefIPD(getContentAreaIPD());
275             } else {
276                 // curLM is a ?
277
//childLC.setStackLimit(MinOptMax.subtract(context
278
// .getStackLimit(), stackSize));
279
childLC.setStackLimit(context.getStackLimit());
280                 childLC.setRefIPD(referenceIPD);
281             }
282
283             // get elements from curLM
284
returnedList = curLM.getNextKnuthElements(childLC, alignment);
285             if (contentList.size() == 0 && childLC.isKeepWithPreviousPending()) {
286                 context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
287                 childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false);
288             }
289             if (returnedList != null
290                     && returnedList.size() == 1
291                     && ((ListElement) returnedList.getFirst()).isForcedBreak()) {
292                 // a descendant of this block has break-before
293
/*
294                 if (returnList.size() == 0) {
295                     // the first child (or its first child ...) has
296                     // break-before;
297                     // all this block, including space before, will be put in
298                     // the
299                     // following page
300                     bSpaceBeforeServed = false;
301                 }*/

302                 contentList.addAll(returnedList);
303
304                 /* extension: conversione di tutta la sequenza fin'ora ottenuta */
305                 if (bpUnit > 0) {
306                     storedList = contentList;
307                     contentList = createUnitElements(contentList);
308                 }
309                 /* end of extension */
310
311                 // "wrap" the Position inside each element
312
// moving the elements from contentList to returnList
313
returnedList = new LinkedList JavaDoc();
314                 wrapPositionElements(contentList, returnList);
315
316                 return returnList;
317             } else {
318                 if (prevLM != null) {
319                     // there is a block handled by prevLM
320
// before the one handled by curLM
321
if (mustKeepTogether()
322                             || context.isKeepWithNextPending()
323                             || childLC.isKeepWithPreviousPending()) {
324                         // Clear keep pending flag
325
context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
326                         // add an infinite penalty to forbid a break between
327
// blocks
328
contentList.add(new BreakElement(
329                                 new Position(this), KnuthElement.INFINITE, context));
330                     } else if (!((ListElement) contentList.getLast()).isGlue()) {
331                         // add a null penalty to allow a break between blocks
332
contentList.add(new BreakElement(
333                                 new Position(this), 0, context));
334                     } else {
335                         // the last element in contentList is a glue;
336
// it is a feasible breakpoint, there is no need to add
337
// a penalty
338
log.warn("glue-type break possibility not handled properly, yet");
339                         //TODO Does this happen? If yes, need to deal with border and padding
340
//at the break possibility
341
}
342                 }
343                 if (returnedList == null || returnedList.size() == 0) {
344                     //Avoid NoSuchElementException below (happens with empty blocks)
345
continue;
346                 }
347                 contentList.addAll(returnedList);
348                 if (((ListElement) returnedList.getLast()).isForcedBreak()) {
349                     // a descendant of this block has break-after
350

351                     /* extension: conversione di tutta la sequenza fin'ora ottenuta */
352                     if (bpUnit > 0) {
353                         storedList = contentList;
354                         contentList = createUnitElements(contentList);
355                     }
356                     /* end of extension */
357
358                     returnedList = new LinkedList JavaDoc();
359                     wrapPositionElements(contentList, returnList);
360
361                     return returnList;
362                 }
363             }
364             // propagate and clear
365
context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, childLC.isKeepWithNextPending());
366             childLC.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
367             childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false);
368             prevLM = curLM;
369         }
370
371         /* Extension: conversione di tutta la sequenza fin'ora ottenuta */
372         if (bpUnit > 0) {
373             storedList = contentList;
374             contentList = createUnitElements(contentList);
375         }
376         /* end of extension */
377
378         returnedList = new LinkedList JavaDoc();
379         if (contentList.size() > 0) {
380             wrapPositionElements(contentList, returnList);
381         } else {
382             //Empty fo:block, zero-length box makes sure the IDs are registered.
383
returnList.add(new KnuthBox(0, notifyPos(new Position(this)), true));
384         }
385
386         addKnuthElementsForBorderPaddingAfter(returnList, true);
387         addKnuthElementsForSpaceAfter(returnList, alignment);
388         addKnuthElementsForBreakAfter(returnList, context);
389
390         if (mustKeepWithNext()) {
391             context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING);
392         }
393         if (mustKeepWithPrevious()) {
394             context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
395         }
396         
397         setFinished(true);
398
399         return returnList;
400     }
401
402     /**
403      * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
404      */

405     public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
406 /*LF*/ //log.debug(" BLM.negotiateBPDAdjustment> " + adj);
407
/*LF*/ //log.debug(" lastElement e' " + (lastElement.isPenalty()
408
// ? "penalty" : (lastElement.isGlue() ? "glue" : "box" )));
409
/*LF*/ //log.debug(" position e' " + lastElement.getPosition().getClass().getName());
410
/*LF*/ //log.debug(" " + (bpUnit > 0 ? "unit" : ""));
411
Position innerPosition = ((NonLeafPosition) lastElement.getPosition()).getPosition();
412
413         if (innerPosition == null && lastElement.isGlue()) {
414             // this adjustment applies to space-before or space-after of this block
415
if (((KnuthGlue) lastElement).getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) {
416                 // this adjustment applies to space-before
417
adjustedSpaceBefore += adj;
418 /*LF*/ //log.debug(" BLM.negotiateBPDAdjustment> spazio prima: " + adj);
419
} else {
420                 // this adjustment applies to space-after
421
adjustedSpaceAfter += adj;
422 /*LF*/ //log.debug(" BLM.negotiateBPDAdjustment> spazio dopo: " + adj);
423
}
424             return adj;
425         } else if (innerPosition instanceof MappingPosition) {
426             // this block has block-progression-unit > 0: the adjustment can concern
427
// - the space-before or space-after of this block,
428
// - the line number of a descendant of this block
429
MappingPosition mappingPos = (MappingPosition)innerPosition;
430             if (lastElement.isGlue()) {
431                 // lastElement is a glue
432
/*LF*/ //log.debug(" BLM.negotiateBPDAdjustment> bpunit con glue");
433
ListIterator JavaDoc storedListIterator = storedList.listIterator(
434                         mappingPos.getFirstIndex());
435                 int newAdjustment = 0;
436                 while (storedListIterator.nextIndex() <= mappingPos.getLastIndex()) {
437                     KnuthElement storedElement = (KnuthElement)storedListIterator.next();
438                     if (storedElement.isGlue()) {
439                         newAdjustment += ((BlockLevelLayoutManager)storedElement
440                                 .getLayoutManager()).negotiateBPDAdjustment(
441                                         adj - newAdjustment, storedElement);
442 /*LF*/ //log.debug(" BLM.negotiateBPDAdjustment> (progressivo) righe: "
443
// + newAdjustment);
444
}
445                 }
446                 newAdjustment = (newAdjustment > 0 ? bpUnit * neededUnits(newAdjustment)
447                                                    : -bpUnit * neededUnits(-newAdjustment));
448                 return newAdjustment;
449             } else {
450                 // lastElement is a penalty: this means that the paragraph
451
// has been split between consecutive pages:
452
// this may involve a change in the number of lines
453
/*LF*/ //log.debug(" BLM.negotiateBPDAdjustment> bpunit con penalty");
454
KnuthPenalty storedPenalty = (KnuthPenalty)
455                                              storedList.get(mappingPos.getLastIndex());
456                 if (storedPenalty.getW() > 0) {
457                     // the original penalty has width > 0
458
/*LF*/ //log.debug(" BLM.negotiateBPDAdjustment> chiamata passata");
459
return ((BlockLevelLayoutManager)storedPenalty.getLayoutManager())
460                            .negotiateBPDAdjustment(storedPenalty.getW(),
461                                    (KnuthElement)storedPenalty);
462                 } else {
463                     // the original penalty has width = 0
464
// the adjustment involves only the spaces before and after
465
/*LF*/ //log.debug(" BLM.negotiateBPDAdjustment> chiamata gestita");
466
return adj;
467                 }
468             }
469         } else if (innerPosition.getLM() != this) {
470             // this adjustment concerns another LM
471
NonLeafPosition savedPos = (NonLeafPosition) lastElement.getPosition();
472             lastElement.setPosition(innerPosition);
473             int returnValue = ((BlockLevelLayoutManager)lastElement.getLayoutManager())
474                     .negotiateBPDAdjustment(adj, lastElement);
475             lastElement.setPosition(savedPos);
476 /*LF*/ //log.debug(" BLM.negotiateBPDAdjustment> righe: " + returnValue);
477
return returnValue;
478         } else {
479             // this should never happen
480
log.error("BlockLayoutManager.negotiateBPDAdjustment(): unexpected Position");
481             return 0;
482         }
483     }
484
485     /**
486      * @see BlockLevelLayoutManager#discardSpace(KnuthGlue)
487      */

488     public void discardSpace(KnuthGlue spaceGlue) {
489         //log.debug(" BLM.discardSpace> " + spaceGlue.getPosition().getClass().getName());
490
Position innerPosition = ((NonLeafPosition) spaceGlue.getPosition()).getPosition();
491
492         if (innerPosition == null || innerPosition.getLM() == this) {
493             // if this block has block-progression-unit > 0, innerPosition can be
494
// a MappingPosition
495
// spaceGlue represents space before or space after of this block
496
if (spaceGlue.getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) {
497                 // space-before must be discarded
498
adjustedSpaceBefore = 0;
499                 foSpaceBefore = new MinOptMax(0);
500             } else {
501                 // space-after must be discarded
502
adjustedSpaceAfter = 0;
503                 foSpaceAfter = new MinOptMax(0);
504                 //TODO Why are both cases handled in the same way?
505
}
506         } else {
507             // this element was not created by this BlockLM
508
NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
509             spaceGlue.setPosition(innerPosition);
510             ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
511             spaceGlue.setPosition(savedPos);
512         }
513     }
514
515     /**
516      * @see LayoutManager#getChangedKnuthElements(List, int)
517      */

518     public LinkedList JavaDoc getChangedKnuthElements(List JavaDoc oldList, int alignment) {
519 /*LF*/ //log.debug("");
520
/*LF*/ //log.debug(" BLM.getChangedKnuthElements> inizio: oldList.size() = "
521
// + oldList.size());
522
ListIterator JavaDoc oldListIterator = oldList.listIterator();
523         KnuthElement returnedElement;
524         KnuthElement currElement = null;
525         KnuthElement prevElement = null;
526         LinkedList JavaDoc returnedList = new LinkedList JavaDoc();
527         LinkedList JavaDoc returnList = new LinkedList JavaDoc();
528         int fromIndex = 0;
529
530         // "unwrap" the Positions stored in the elements
531
KnuthElement oldElement = null;
532         while (oldListIterator.hasNext()) {
533             oldElement = (KnuthElement)oldListIterator.next();
534             Position innerPosition = ((NonLeafPosition) oldElement.getPosition()).getPosition();
535             //log.debug(" BLM> unwrapping: "
536
// + (oldElement.isBox() ? "box " : (oldElement.isGlue() ? "glue " : "penalty"))
537
// + " creato da " + oldElement.getLayoutManager().getClass().getName());
538
//log.debug(" BLM> unwrapping: "
539
// + oldElement.getPosition().getClass().getName());
540
if (innerPosition != null) {
541                 // oldElement was created by a descendant of this BlockLM
542
oldElement.setPosition(innerPosition);
543             } else {
544                 // thisElement was created by this BlockLM
545
// modify its position in order to recognize it was not created
546
// by a child
547
oldElement.setPosition(new Position(this));
548             }
549         }
550
551         // create the iterator
552
List JavaDoc workList;
553         if (bpUnit == 0) {
554             workList = oldList;
555         } else {
556             // the storedList must be used instead of oldList;
557
// find the index of the first element of returnedList
558
// corresponding to the first element of oldList
559
oldListIterator = oldList.listIterator();
560             KnuthElement el = (KnuthElement) oldListIterator.next();
561             while (!(el.getPosition() instanceof MappingPosition)) {
562                 el = (KnuthElement) oldListIterator.next();
563             }
564             int iFirst = ((MappingPosition) el.getPosition()).getFirstIndex();
565
566             // find the index of the last element of returnedList
567
// corresponding to the last element of oldList
568
oldListIterator = oldList.listIterator(oldList.size());
569             el = (KnuthElement) oldListIterator.previous();
570             while (!(el.getPosition() instanceof MappingPosition)) {
571                 el = (KnuthElement) oldListIterator.previous();
572             }
573             int iLast = ((MappingPosition) el.getPosition()).getLastIndex();
574
575             //log-debug(" si usa storedList da " + iFirst + " a " + iLast
576
// + " compresi su " + storedList.size() + " elementi totali");
577
workList = storedList.subList(iFirst, iLast + 1);
578         }
579         ListIterator JavaDoc workListIterator = workList.listIterator();
580
581         //log.debug(" BLM.getChangedKnuthElements> workList.size() = "
582
// + workList.size() + " da 0 a " + (workList.size() - 1));
583

584         while (workListIterator.hasNext()) {
585             currElement = (KnuthElement) workListIterator.next();
586             //log.debug("elemento n. " + workListIterator.previousIndex()
587
// + " nella workList");
588
if (prevElement != null
589                 && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
590                 // prevElement is the last element generated by the same LM
591
BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
592                                                  prevElement.getLayoutManager();
593                 BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
594                                                  currElement.getLayoutManager();
595                 boolean bSomethingAdded = false;
596                 if (prevLM != this) {
597                     //log.debug(" BLM.getChangedKnuthElements> chiamata da "
598
// + fromIndex + " a " + workListIterator.previousIndex() + " su "
599
// + prevLM.getClass().getName());
600
returnedList.addAll(prevLM.getChangedKnuthElements(workList.subList(
601                                 fromIndex, workListIterator.previousIndex()), alignment));
602                     bSomethingAdded = true;
603                 } else {
604                     // prevLM == this
605
// do nothing
606
//log.debug(" BLM.getChangedKnuthElements> elementi propri, "
607
// + "ignorati, da " + fromIndex + " a " + workListIterator.previousIndex()
608
// + " su " + prevLM.getClass().getName());
609
}
610                 fromIndex = workListIterator.previousIndex();
611
612                 /*
613                  * TODO: why are KnuthPenalties added here,
614                  * while in getNextKE they were changed to BreakElements?
615                  */

616                 // there is another block after this one
617
if (bSomethingAdded
618                     && (this.mustKeepTogether()
619                         || prevLM.mustKeepWithNext()
620                         || currLM.mustKeepWithPrevious())) {
621                     // add an infinite penalty to forbid a break between blocks
622
returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
623                             new Position(this), false));
624                 } else if (bSomethingAdded && !((KnuthElement) returnedList.getLast()).isGlue()) {
625                     // add a null penalty to allow a break between blocks
626
returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
627                 }
628             }
629             prevElement = currElement;
630         }
631         if (currElement != null) {
632             BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
633                                              currElement.getLayoutManager();
634             if (currLM != this) {
635                 //log.debug(" BLM.getChangedKnuthElements> chiamata da " + fromIndex
636
// + " a " + oldList.size() + " su " + currLM.getClass().getName());
637
returnedList.addAll(currLM.getChangedKnuthElements(
638                         workList.subList(fromIndex, workList.size()), alignment));
639             } else {
640                 // currLM == this
641
// there are no more elements to add
642
// remove the last penalty added to returnedList
643
if (returnedList.size() > 0) {
644                     returnedList.removeLast();
645                 }
646                 //log.debug(" BLM.getChangedKnuthElements> elementi propri, ignorati, da "
647
// + fromIndex + " a " + workList.size());
648
}
649         }
650
651         // append elements representing space-before
652
boolean spaceBeforeIsConditional = true;
653         if (fobj instanceof org.apache.fop.fo.flow.Block) {
654             spaceBeforeIsConditional = ((org.apache.fop.fo.flow.Block)fobj)
655                     .getCommonMarginBlock().spaceBefore.getSpace().isDiscard();
656         }
657         if (bpUnit > 0
658             || adjustedSpaceBefore != 0) {
659             if (!spaceBeforeIsConditional) {
660                 // add elements to prevent the glue to be discarded
661
returnList.add(new KnuthBox(0,
662                                             new NonLeafPosition(this, null), false));
663                 returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
664                                                 new NonLeafPosition(this, null), false));
665             }
666             if (bpUnit > 0) {
667                 returnList.add(new KnuthGlue(0, 0, 0,
668                     SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(this, null), true));
669             } else {
670                 returnList.add(new KnuthGlue(adjustedSpaceBefore, 0, 0,
671                     SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(this, null), true));
672             }
673         }
674
675         //log.debug(" BLM.getChangedKnuthElements> intermedio: returnedList.size() = "
676
// + returnedList.size());
677

678 /* estensione: conversione complessiva */
679 /*LF*/ if (bpUnit > 0) {
680 /*LF*/ storedList = returnedList;
681 /*LF*/ returnedList = createUnitElements(returnedList);
682 /*LF*/ }
683 /* estensione */
684
685         // "wrap" the Position stored in each element of returnedList
686
// and add elements to returnList
687
ListIterator JavaDoc listIter = returnedList.listIterator();
688         while (listIter.hasNext()) {
689             returnedElement = (KnuthElement)listIter.next();
690             returnedElement.setPosition(new NonLeafPosition(this, returnedElement.getPosition()));
691             returnList.add(returnedElement);
692         }
693
694         // append elements representing space-after
695
boolean spaceAfterIsConditional = true;
696         if (fobj instanceof org.apache.fop.fo.flow.Block) {
697             spaceAfterIsConditional = ((org.apache.fop.fo.flow.Block)fobj)
698                         .getCommonMarginBlock().spaceAfter.getSpace().isDiscard();
699         }
700         if (bpUnit > 0 || adjustedSpaceAfter != 0) {
701             if (!spaceAfterIsConditional) {
702                 returnList.add(new KnuthPenalty(0,
703                         KnuthElement.INFINITE, false,
704                         new NonLeafPosition(this, null), false));
705             }
706             if (bpUnit > 0) {
707                 returnList.add(new KnuthGlue(0, 0, 0,
708                         SPACE_AFTER_ADJUSTMENT,
709                         new NonLeafPosition(this, null),
710                         (!spaceAfterIsConditional) ? false : true));
711             } else {
712                 returnList.add(new KnuthGlue(adjustedSpaceAfter, 0, 0,
713                         SPACE_AFTER_ADJUSTMENT,
714                         new NonLeafPosition(this, null),
715                         (!spaceAfterIsConditional) ? false : true));
716             }
717             if (!spaceAfterIsConditional) {
718                 returnList.add(new KnuthBox(0,
719                         new NonLeafPosition(this, null), true));
720             }
721         }
722
723         //log.debug(" BLM.getChangedKnuthElements> finished: returnList.size() = "
724
// + returnList.size());
725
return returnList;
726     }
727
728     /**
729      * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
730      */

731     // default action: ask parentLM
732
public boolean mustKeepTogether() {
733         return ((getParent() instanceof BlockLevelLayoutManager
734                     && ((BlockLevelLayoutManager) getParent()).mustKeepTogether())
735                 || (getParent() instanceof InlineLayoutManager
736                     && ((InlineLayoutManager) getParent()).mustKeepTogether()));
737     }
738
739     /**
740      * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
741      */

742     public boolean mustKeepWithPrevious() {
743         return false;
744     }
745
746     /**
747      * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
748      */

749     public boolean mustKeepWithNext() {
750         return false;
751     }
752
753     /**
754      * Adds the unresolved elements for border and padding to a layout context so break
755      * possibilities can be properly constructed.
756      * @param context the layout context
757      */

758     protected void addPendingMarks(LayoutContext context) {
759         CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
760         if (borderAndPadding != null) {
761             if (borderAndPadding.getBorderBeforeWidth(false) > 0) {
762                 context.addPendingBeforeMark(new BorderElement(
763                         getAuxiliaryPosition(),
764                         borderAndPadding.getBorderInfo(
765                                 CommonBorderPaddingBackground.BEFORE).getWidth(),
766                                 RelSide.BEFORE,
767                                 false, false, this));
768             }
769             if (borderAndPadding.getPaddingBefore(false, this) > 0) {
770                 context.addPendingBeforeMark(new PaddingElement(
771                         getAuxiliaryPosition(),
772                         borderAndPadding.getPaddingLengthProperty(
773                                 CommonBorderPaddingBackground.BEFORE),
774                                 RelSide.BEFORE,
775                                 false, false, this));
776             }
777             if (borderAndPadding.getBorderAfterWidth(false) > 0) {
778                 context.addPendingAfterMark(new BorderElement(
779                         getAuxiliaryPosition(),
780                         borderAndPadding.getBorderInfo(
781                                 CommonBorderPaddingBackground.AFTER).getWidth(),
782                                 RelSide.AFTER,
783                                 false, false, this));
784             }
785             if (borderAndPadding.getPaddingAfter(false, this) > 0) {
786                 context.addPendingAfterMark(new PaddingElement(
787                         getAuxiliaryPosition(),
788                         borderAndPadding.getPaddingLengthProperty(
789                                 CommonBorderPaddingBackground.AFTER),
790                                 RelSide.AFTER,
791                                 false, false, this));
792             }
793         }
794     }
795     
796     /** @return the border, padding and background info structure */
797     private CommonBorderPaddingBackground getBorderPaddingBackground() {
798         if (fobj instanceof org.apache.fop.fo.flow.Block) {
799             return ((org.apache.fop.fo.flow.Block)fobj)
800                 .getCommonBorderPaddingBackground();
801         } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
802             return ((org.apache.fop.fo.flow.BlockContainer)fobj)
803                 .getCommonBorderPaddingBackground();
804         } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
805             return ((org.apache.fop.fo.flow.ListBlock)fobj)
806                 .getCommonBorderPaddingBackground();
807         } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
808             return ((org.apache.fop.fo.flow.ListItem)fobj)
809                 .getCommonBorderPaddingBackground();
810         } else if (fobj instanceof org.apache.fop.fo.flow.Table) {
811             return ((org.apache.fop.fo.flow.Table)fobj)
812                 .getCommonBorderPaddingBackground();
813         } else {
814             return null;
815         }
816     }
817     
818     /** @return the space-before property */
819     private SpaceProperty getSpaceBeforeProperty() {
820         if (fobj instanceof org.apache.fop.fo.flow.Block) {
821             return ((org.apache.fop.fo.flow.Block)fobj)
822                 .getCommonMarginBlock().spaceBefore;
823         } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
824             return ((org.apache.fop.fo.flow.BlockContainer)fobj)
825                 .getCommonMarginBlock().spaceBefore;
826         } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
827             return ((org.apache.fop.fo.flow.ListBlock)fobj)
828                 .getCommonMarginBlock().spaceBefore;
829         } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
830             return ((org.apache.fop.fo.flow.ListItem)fobj)
831                 .getCommonMarginBlock().spaceBefore;
832         } else if (fobj instanceof org.apache.fop.fo.flow.Table) {
833             return ((org.apache.fop.fo.flow.Table)fobj)
834                 .getCommonMarginBlock().spaceBefore;
835         } else {
836             return null;
837         }
838     }
839     
840     /** @return the space-after property */
841     private SpaceProperty getSpaceAfterProperty() {
842         if (fobj instanceof org.apache.fop.fo.flow.Block) {
843             return ((org.apache.fop.fo.flow.Block)fobj)
844                 .getCommonMarginBlock().spaceAfter;
845         } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
846             return ((org.apache.fop.fo.flow.BlockContainer)fobj)
847                 .getCommonMarginBlock().spaceAfter;
848         } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
849             return ((org.apache.fop.fo.flow.ListBlock)fobj)
850                 .getCommonMarginBlock().spaceAfter;
851         } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
852             return ((org.apache.fop.fo.flow.ListItem)fobj)
853                 .getCommonMarginBlock().spaceAfter;
854         } else if (fobj instanceof org.apache.fop.fo.flow.Table) {
855             return ((org.apache.fop.fo.flow.Table)fobj)
856                 .getCommonMarginBlock().spaceAfter;
857         } else {
858             return null;
859         }
860     }
861     
862     /**
863      * Creates Knuth elements for before border padding and adds them to the return list.
864      * @param returnList return list to add the additional elements to
865      * @param isFirst true if this is the first time a layout manager instance needs to generate
866      * border and padding
867      */

868     protected void addKnuthElementsForBorderPaddingBefore(LinkedList JavaDoc returnList, boolean isFirst) {
869         //Border and Padding (before)
870
CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
871         if (borderAndPadding != null) {
872             if (borderAndPadding.getBorderBeforeWidth(false) > 0) {
873                 returnList.add(new BorderElement(
874                         getAuxiliaryPosition(),
875                         borderAndPadding.getBorderInfo(CommonBorderPaddingBackground.BEFORE)
876                                 .getWidth(),
877                         RelSide.BEFORE, isFirst, false, this));
878             }
879             if (borderAndPadding.getPaddingBefore(false, this) > 0) {
880                 returnList.add(new PaddingElement(
881                         getAuxiliaryPosition(),
882                         borderAndPadding.getPaddingLengthProperty(
883                                 CommonBorderPaddingBackground.BEFORE),
884                         RelSide.BEFORE, isFirst, false, this));
885             }
886         }
887     }
888
889     /**
890      * Creates Knuth elements for after border padding and adds them to the return list.
891      * @param returnList return list to add the additional elements to
892      * @param isLast true if this is the last time a layout manager instance needs to generate
893      * border and padding
894      */

895     protected void addKnuthElementsForBorderPaddingAfter(LinkedList JavaDoc returnList, boolean isLast) {
896         //Border and Padding (after)
897
CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
898         if (borderAndPadding != null) {
899             if (borderAndPadding.getPaddingAfter(false, this) > 0) {
900                 returnList.add(new PaddingElement(
901                         getAuxiliaryPosition(),
902                         borderAndPadding.getPaddingLengthProperty(
903                                 CommonBorderPaddingBackground.AFTER),
904                         RelSide.AFTER, false, isLast, this));
905             }
906             if (borderAndPadding.getBorderAfterWidth(false) > 0) {
907                 returnList.add(new BorderElement(
908                         getAuxiliaryPosition(),
909                         borderAndPadding.getBorderInfo(CommonBorderPaddingBackground.AFTER)
910                                 .getWidth(),
911                         RelSide.AFTER, false, isLast, this));
912             }
913         }
914     }
915
916     /**
917      * Creates Knuth elements for break-before and adds them to the return list.
918      * @param returnList return list to add the additional elements to
919      * @param context the layout context
920      * @return true if an element has been added due to a break-before.
921      */

922     protected boolean addKnuthElementsForBreakBefore(LinkedList JavaDoc returnList,
923             LayoutContext context) {
924         int breakBefore = -1;
925         if (fobj instanceof org.apache.fop.fo.flow.Block) {
926             breakBefore = ((org.apache.fop.fo.flow.Block) fobj).getBreakBefore();
927         } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
928             breakBefore = ((org.apache.fop.fo.flow.BlockContainer) fobj).getBreakBefore();
929         } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
930             breakBefore = ((org.apache.fop.fo.flow.ListBlock) fobj).getBreakBefore();
931         } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
932             breakBefore = ((org.apache.fop.fo.flow.ListItem) fobj).getBreakBefore();
933         } else if (fobj instanceof org.apache.fop.fo.flow.Table) {
934             breakBefore = ((org.apache.fop.fo.flow.Table) fobj).getBreakBefore();
935         }
936         if (breakBefore == EN_PAGE
937                 || breakBefore == EN_COLUMN
938                 || breakBefore == EN_EVEN_PAGE
939                 || breakBefore == EN_ODD_PAGE) {
940             // return a penalty element, representing a forced page break
941
returnList.add(new BreakElement(getAuxiliaryPosition(),
942                     0, -KnuthElement.INFINITE, breakBefore, context));
943             return true;
944         } else {
945             return false;
946         }
947     }
948
949     /**
950      * Creates Knuth elements for break-after and adds them to the return list.
951      * @param returnList return list to add the additional elements to
952      * @param context the layout context
953      * @return true if an element has been added due to a break-after.
954      */

955     protected boolean addKnuthElementsForBreakAfter(LinkedList JavaDoc returnList,
956             LayoutContext context) {
957         int breakAfter = -1;
958         if (fobj instanceof org.apache.fop.fo.flow.Block) {
959             breakAfter = ((org.apache.fop.fo.flow.Block) fobj).getBreakAfter();
960         } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
961             breakAfter = ((org.apache.fop.fo.flow.BlockContainer) fobj).getBreakAfter();
962         } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
963             breakAfter = ((org.apache.fop.fo.flow.ListBlock) fobj).getBreakAfter();
964         } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
965             breakAfter = ((org.apache.fop.fo.flow.ListItem) fobj).getBreakAfter();
966         } else if (fobj instanceof org.apache.fop.fo.flow.Table) {
967             breakAfter = ((org.apache.fop.fo.flow.Table) fobj).getBreakAfter();
968         }
969         if (breakAfter == EN_PAGE
970                 || breakAfter == EN_COLUMN
971                 || breakAfter == EN_EVEN_PAGE
972                 || breakAfter == EN_ODD_PAGE) {
973             // add a penalty element, representing a forced page break
974
returnList.add(new BreakElement(getAuxiliaryPosition(),
975                     0, -KnuthElement.INFINITE, breakAfter, context));
976             return true;
977         } else {
978             return false;
979         }
980     }
981
982     /**
983      * Creates Knuth elements for space-before and adds them to the return list.
984      * @param returnList return list to add the additional elements to
985      * @param alignment vertical alignment
986      */

987     protected void addKnuthElementsForSpaceBefore(LinkedList JavaDoc returnList/*,
988             Position returnPosition*/
, int alignment) {
989         SpaceProperty spaceBefore = getSpaceBeforeProperty();
990         // append elements representing space-before
991
if (spaceBefore != null
992                    && !(spaceBefore.getMinimum(this).getLength().getValue(this) == 0
993                         && spaceBefore.getMaximum(this).getLength().getValue(this) == 0)) {
994             returnList.add(new SpaceElement(getAuxiliaryPosition(), spaceBefore,
995                     RelSide.BEFORE,
996                     true, false, this));
997         }
998         /*
999         if (bpUnit > 0
1000                || spaceBefore != null
1001                   && !(spaceBefore.getMinimum(this).getLength().getValue(this) == 0
1002                        && spaceBefore.getMaximum(this).getLength().getValue(this) == 0)) {
1003            if (spaceBefore != null && !spaceBefore.getSpace().isDiscard()) {
1004                // add elements to prevent the glue to be discarded
1005                returnList.add(new KnuthBox(0, getAuxiliaryPosition(), false));
1006                returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
1007                        false, getAuxiliaryPosition(), false));
1008            }
1009            if (bpUnit > 0) {
1010                returnList.add(new KnuthGlue(0, 0, 0,
1011                        BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
1012                        getAuxiliaryPosition(), true));
1013            } else { //if (alignment == EN_JUSTIFY) {
1014                returnList.add(new KnuthGlue(
1015                        spaceBefore.getOptimum(this).getLength().getValue(this),
1016                        spaceBefore.getMaximum(this).getLength().getValue(this)
1017                                - spaceBefore.getOptimum(this).getLength().getValue(this),
1018                        spaceBefore.getOptimum(this).getLength().getValue(this)
1019                                - spaceBefore.getMinimum(this).getLength().getValue(this),
1020                        BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
1021                        getAuxiliaryPosition(), true));
1022// } else {
1023// returnList.add(new KnuthGlue(
1024// spaceBefore.getOptimum().getLength().getValue(this),
1025// 0, 0, BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
1026// returnPosition, true));
1027            }
1028        }*/

1029    }
1030
1031    /**
1032     * Creates Knuth elements for space-after and adds them to the return list.
1033     * @param returnList return list to add the additional elements to
1034     * @param alignment vertical alignment
1035     */

1036    protected void addKnuthElementsForSpaceAfter(LinkedList JavaDoc returnList/*, Position returnPosition*/,
1037                int alignment) {
1038        SpaceProperty spaceAfter = getSpaceAfterProperty();
1039        // append elements representing space-after
1040
if (spaceAfter != null
1041                && !(spaceAfter.getMinimum(this).getLength().getValue(this) == 0
1042                     && spaceAfter.getMaximum(this).getLength().getValue(this) == 0)) {
1043            returnList.add(new SpaceElement(getAuxiliaryPosition(), spaceAfter,
1044                    RelSide.AFTER,
1045                    false, true, this));
1046        }
1047        /*
1048        if (bpUnit > 0
1049                || spaceAfter != null
1050                   && !(spaceAfter.getMinimum(this).getLength().getValue(this) == 0
1051                        && spaceAfter.getMaximum(this).getLength().getValue(this) == 0)) {
1052            if (spaceAfter != null && !spaceAfter.getSpace().isDiscard()) {
1053                returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
1054                        false, getAuxiliaryPosition(), false));
1055            }
1056            if (bpUnit > 0) {
1057                returnList.add(new KnuthGlue(0, 0, 0,
1058                        BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT,
1059                        getAuxiliaryPosition(), true));
1060            } else { //if (alignment == EN_JUSTIFY) {
1061                returnList.add(new KnuthGlue(
1062                        spaceAfter.getOptimum(this).getLength().getValue(this),
1063                        spaceAfter.getMaximum(this).getLength().getValue(this)
1064                                - spaceAfter.getOptimum(this).getLength().getValue(this),
1065                        spaceAfter.getOptimum(this).getLength().getValue(this)
1066                                - spaceAfter.getMinimum(this).getLength().getValue(this),
1067                        BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, getAuxiliaryPosition(),
1068                        (!spaceAfter.getSpace().isDiscard()) ? false : true));
1069// } else {
1070// returnList.add(new KnuthGlue(
1071// spaceAfter.getOptimum().getLength().getValue(this), 0, 0,
1072// BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, returnPosition,
1073// (!spaceAfter.getSpace().isDiscard()) ? false : true));
1074            }
1075            if (spaceAfter != null && !spaceAfter.getSpace().isDiscard()) {
1076                returnList.add(new KnuthBox(0, getAuxiliaryPosition(), true));
1077            }
1078        }*/

1079    }
1080
1081    protected LinkedList JavaDoc createUnitElements(LinkedList JavaDoc oldList) {
1082        //log.debug("Start conversion: " + oldList.size()
1083
// + " elements, space-before.min=" + layoutProps.spaceBefore.getSpace().min
1084
// + " space-after.min=" + layoutProps.spaceAfter.getSpace().min);
1085
// add elements at the beginning and at the end of oldList
1086
// representing minimum spaces
1087
LayoutManager lm = ((KnuthElement)oldList.getFirst()).getLayoutManager();
1088        boolean bAddedBoxBefore = false;
1089        boolean bAddedBoxAfter = false;
1090        if (adjustedSpaceBefore > 0) {
1091            oldList.addFirst(new KnuthBox(adjustedSpaceBefore,
1092                                          new Position(lm), true));
1093            bAddedBoxBefore = true;
1094        }
1095        if (adjustedSpaceAfter > 0) {
1096            oldList.addLast(new KnuthBox(adjustedSpaceAfter,
1097                                         new Position(lm), true));
1098            bAddedBoxAfter = true;
1099        }
1100
1101        MinOptMax totalLength = new MinOptMax(0);
1102        MinOptMax totalUnits = new MinOptMax(0);
1103        LinkedList JavaDoc newList = new LinkedList JavaDoc();
1104
1105        //log.debug(" Prima scansione");
1106
// scan the list once to compute total min, opt and max length
1107
ListIterator JavaDoc oldListIterator = oldList.listIterator();
1108        while (oldListIterator.hasNext()) {
1109            KnuthElement element = (KnuthElement) oldListIterator.next();
1110            if (element.isBox()) {
1111                totalLength.add(new MinOptMax(element.getW()));
1112                //log.debug("box " + element.getW());
1113
} else if (element.isGlue()) {
1114                totalLength.min -= ((KnuthGlue) element).getZ();
1115                totalLength.max += ((KnuthGlue) element).getY();
1116                //leafValue = ((LeafPosition) element.getPosition()).getLeafPos();
1117
//log.debug("glue " + element.getW() + " + "
1118
// + ((KnuthGlue) element).getY() + " - " + ((KnuthGlue) element).getZ());
1119
} else {
1120                //log.debug((((KnuthPenalty)element).getP() == KnuthElement.INFINITE
1121
// ? "PENALTY " : "penalty ") + element.getW());
1122
}
1123        }
1124        // compute the total amount of "units"
1125
totalUnits = new MinOptMax(neededUnits(totalLength.min),
1126                                   neededUnits(totalLength.opt),
1127                                   neededUnits(totalLength.max));
1128        //log.debug(" totalLength= " + totalLength);
1129
//log.debug(" unita'= " + totalUnits);
1130

1131        //log.debug(" Seconda scansione");
1132
// scan the list once more, stopping at every breaking point
1133
// in order to compute partial min, opt and max length
1134
// and create the new elements
1135
oldListIterator = oldList.listIterator();
1136        boolean bPrevIsBox = false;
1137        MinOptMax lengthBeforeBreak = new MinOptMax(0);
1138        MinOptMax lengthAfterBreak = (MinOptMax) totalLength.clone();
1139        MinOptMax unitsBeforeBreak;
1140        MinOptMax unitsAfterBreak;
1141        MinOptMax unsuppressibleUnits = new MinOptMax(0);
1142        int firstIndex = 0;
1143        int lastIndex = -1;
1144        while (oldListIterator.hasNext()) {
1145            KnuthElement element = (KnuthElement) oldListIterator.next();
1146            lastIndex++;
1147            if (element.isBox()) {
1148                lengthBeforeBreak.add(new MinOptMax(element.getW()));
1149                lengthAfterBreak.subtract(new MinOptMax(element.getW()));
1150                bPrevIsBox = true;
1151            } else if (element.isGlue()) {
1152                lengthBeforeBreak.min -= ((KnuthGlue) element).getZ();
1153                lengthAfterBreak.min += ((KnuthGlue) element).getZ();
1154                lengthBeforeBreak.max += ((KnuthGlue) element).getY();
1155                lengthAfterBreak.max -= ((KnuthGlue) element).getY();
1156                bPrevIsBox = false;
1157            } else {
1158                lengthBeforeBreak.add(new MinOptMax(element.getW()));
1159                bPrevIsBox = false;
1160            }
1161
1162            // create the new elements
1163
if (element.isPenalty() && ((KnuthPenalty) element).getP() < KnuthElement.INFINITE
1164                    || element.isGlue() && bPrevIsBox
1165                    || !oldListIterator.hasNext()) {
1166                // suppress elements after the breaking point
1167
int iStepsForward = 0;
1168                while (oldListIterator.hasNext()) {
1169                    KnuthElement el = (KnuthElement) oldListIterator.next();
1170                    iStepsForward++;
1171                    if (el.isGlue()) {
1172                        // suppressed glue
1173
lengthAfterBreak.min += ((KnuthGlue) el).getZ();
1174                        lengthAfterBreak.max -= ((KnuthGlue) el).getY();
1175                    } else if (el.isPenalty()) {
1176                        // suppressed penalty, do nothing
1177
} else {
1178                        // box, end of suppressions
1179
break;
1180                    }
1181                }
1182                // compute the partial amount of "units" before and after the break
1183
unitsBeforeBreak = new MinOptMax(neededUnits(lengthBeforeBreak.min),
1184                                                 neededUnits(lengthBeforeBreak.opt),
1185                                                 neededUnits(lengthBeforeBreak.max));
1186                unitsAfterBreak = new MinOptMax(neededUnits(lengthAfterBreak.min),
1187                                                neededUnits(lengthAfterBreak.opt),
1188                                                neededUnits(lengthAfterBreak.max));
1189
1190                // rewind the iterator and lengthAfterBreak
1191
for (int i = 0; i < iStepsForward; i++) {
1192                    KnuthElement el = (KnuthElement) oldListIterator.previous();
1193                    if (el.isGlue()) {
1194                        lengthAfterBreak.min -= ((KnuthGlue) el).getZ();
1195                        lengthAfterBreak.max += ((KnuthGlue) el).getY();
1196                    }
1197                }
1198
1199                // compute changes in length, stretch and shrink
1200
int uLengthChange = unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt;
1201                int uStretchChange = (unitsBeforeBreak.max + unitsAfterBreak.max - totalUnits.max)
1202                    - (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt);
1203                int uShrinkChange = (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt)
1204                    - (unitsBeforeBreak.min + unitsAfterBreak.min - totalUnits.min);
1205
1206                // compute the number of normal, stretch and shrink unit
1207
// that must be added to the new sequence
1208
int uNewNormal = unitsBeforeBreak.opt - unsuppressibleUnits.opt;
1209                int uNewStretch = (unitsBeforeBreak.max - unitsBeforeBreak.opt)
1210                                  - (unsuppressibleUnits.max - unsuppressibleUnits.opt);
1211                int uNewShrink = (unitsBeforeBreak.opt - unitsBeforeBreak.min)
1212                                 - (unsuppressibleUnits.opt - unsuppressibleUnits.min);
1213
1214                //log.debug("("
1215
// + unsuppressibleUnits.min + "-" + unsuppressibleUnits.opt + "-"
1216
// + unsuppressibleUnits.max + ") "
1217
// + " -> " + unitsBeforeBreak.min + "-" + unitsBeforeBreak.opt + "-"
1218
// + unitsBeforeBreak.max
1219
// + " + " + unitsAfterBreak.min + "-" + unitsAfterBreak.opt + "-"
1220
// + unitsAfterBreak.max
1221
// + (uLengthChange != 0 ? " [length " + uLengthChange + "] " : "")
1222
// + (uStretchChange != 0 ? " [stretch " + uStretchChange + "] " : "")
1223
// + (uShrinkChange != 0 ? " [shrink " + uShrinkChange + "]" : ""));
1224

1225                // create the MappingPosition which will be stored in the new elements
1226
// correct firstIndex and lastIndex
1227
int firstIndexCorrection = 0;
1228                int lastIndexCorrection = 0;
1229                if (bAddedBoxBefore) {
1230                    if (firstIndex != 0) {
1231                        firstIndexCorrection++;
1232                    }
1233                    lastIndexCorrection++;
1234                }
1235                if (bAddedBoxAfter && lastIndex == (oldList.size() - 1)) {
1236                    lastIndexCorrection++;
1237                }
1238                MappingPosition mappingPos = new MappingPosition(this,
1239                                                                 firstIndex - firstIndexCorrection,
1240                                                                 lastIndex - lastIndexCorrection);
1241
1242                // new box
1243
newList.add(new KnuthBox((uNewNormal - uLengthChange) * bpUnit,
1244                                         mappingPos,
1245                                         false));
1246                unsuppressibleUnits.add(new MinOptMax(uNewNormal - uLengthChange));
1247                //log.debug(" box " + (uNewNormal - uLengthChange));
1248

1249                // new infinite penalty, glue and box, if necessary
1250
if (uNewStretch - uStretchChange > 0
1251                    || uNewShrink - uShrinkChange > 0) {
1252                    int iStretchUnits = (uNewStretch - uStretchChange > 0
1253                            ? (uNewStretch - uStretchChange) : 0);
1254                    int iShrinkUnits = (uNewShrink - uShrinkChange > 0
1255                            ? (uNewShrink - uShrinkChange) : 0);
1256                    newList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
1257                                                 mappingPos,
1258                                                 false));
1259                    newList.add(new KnuthGlue(0,
1260                                              iStretchUnits * bpUnit,
1261                                              iShrinkUnits * bpUnit,
1262                                              LINE_NUMBER_ADJUSTMENT,
1263                                              mappingPos,
1264                                              false));
1265                    //log.debug(" PENALTY");
1266
//log.debug(" glue 0 " + iStretchUnits + " " + iShrinkUnits);
1267
unsuppressibleUnits.max += iStretchUnits;
1268                    unsuppressibleUnits.min -= iShrinkUnits;
1269                    if (!oldListIterator.hasNext()) {
1270                        newList.add(new KnuthBox(0,
1271                                                 mappingPos,
1272                                                 false));
1273                        //log.debug(" box 0");
1274
}
1275                }
1276
1277                // new breaking sequence
1278
if (uStretchChange != 0
1279                    || uShrinkChange != 0) {
1280                    // new infinite penalty, glue, penalty and glue
1281
newList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
1282                                                 mappingPos,
1283                                                 false));
1284                    newList.add(new KnuthGlue(0,
1285                                              uStretchChange * bpUnit,
1286                                              uShrinkChange * bpUnit,
1287                                              LINE_NUMBER_ADJUSTMENT,
1288                                              mappingPos,
1289                                              false));
1290                    newList.add(new KnuthPenalty(uLengthChange * bpUnit,
1291                                                 0, false, element.getPosition(), false));
1292                    newList.add(new KnuthGlue(0,
1293                                              -uStretchChange * bpUnit,
1294                                              -uShrinkChange * bpUnit,
1295                                              LINE_NUMBER_ADJUSTMENT,
1296                                              mappingPos,
1297                                              false));
1298                    //log.debug(" PENALTY");
1299
//log.debug(" glue 0 " + uStretchChange + " " + uShrinkChange);
1300
//log.debug(" penalty " + uLengthChange + " * unit");
1301
//log.debug(" glue 0 " + (- uStretchChange) + " "
1302
// + (- uShrinkChange));
1303
} else if (oldListIterator.hasNext()) {
1304                    // new penalty
1305
newList.add(new KnuthPenalty(uLengthChange * bpUnit,
1306                                                 0, false,
1307                                                 mappingPos,
1308                                                 false));
1309                    //log.debug(" penalty " + uLengthChange + " * unit");
1310
}
1311                // update firstIndex
1312
firstIndex = lastIndex + 1;
1313            }
1314
1315            if (element.isPenalty()) {
1316                lengthBeforeBreak.add(new MinOptMax(-element.getW()));
1317            }
1318
1319        }
1320
1321        // remove elements at the beginning and at the end of oldList
1322
// representing minimum spaces
1323
if (adjustedSpaceBefore > 0) {
1324            oldList.removeFirst();
1325        }
1326        if (adjustedSpaceAfter > 0) {
1327            oldList.removeLast();
1328        }
1329
1330        // if space-before.conditionality is "discard", correct newList
1331
boolean correctFirstElement = false;
1332        if (fobj instanceof org.apache.fop.fo.flow.Block) {
1333            correctFirstElement = ((org.apache.fop.fo.flow.Block)fobj)
1334                .getCommonMarginBlock().spaceBefore.getSpace().isDiscard();
1335        }
1336        if (correctFirstElement) {
1337            // remove the wrong element
1338
KnuthBox wrongBox = (KnuthBox) newList.removeFirst();
1339            // if this paragraph is at the top of a page, the space before
1340
// must be ignored; compute the length change
1341
int decreasedLength = (neededUnits(totalLength.opt)
1342                                   - neededUnits(totalLength.opt - adjustedSpaceBefore))
1343                                  * bpUnit;
1344            // insert the correct elements
1345
newList.addFirst(new KnuthBox(wrongBox.getW() - decreasedLength,
1346                                          wrongBox.getPosition(), false));
1347            newList.addFirst(new KnuthGlue(decreasedLength, 0, 0, SPACE_BEFORE_ADJUSTMENT,
1348                                           wrongBox.getPosition(), false));
1349            //log.debug(" rimosso box " + neededUnits(wrongBox.getW()));
1350
//log.debug(" aggiunto glue " + neededUnits(decreasedLength) + " 0 0");
1351
//log.debug(" aggiunto box " + neededUnits(
1352
// wrongBox.getW() - decreasedLength));
1353
}
1354
1355        // if space-after.conditionality is "discard", correct newList
1356
boolean correctLastElement = false;
1357        if (fobj instanceof org.apache.fop.fo.flow.Block) {
1358            correctLastElement = ((org.apache.fop.fo.flow.Block)fobj)
1359                    .getCommonMarginBlock().spaceAfter.getSpace().isDiscard();
1360        }
1361        if (correctLastElement) {
1362            // remove the wrong element
1363
KnuthBox wrongBox = (KnuthBox) newList.removeLast();
1364            // if the old sequence is box(h) penalty(inf) glue(x,y,z) box(0)
1365
// (it cannot be parted and has some stretch or shrink)
1366
// the wrong box is the first one, not the last one
1367
LinkedList JavaDoc preserveList = new LinkedList JavaDoc();
1368            if (wrongBox.getW() == 0) {
1369                preserveList.add(wrongBox);
1370                preserveList.addFirst((KnuthGlue) newList.removeLast());
1371                preserveList.addFirst((KnuthPenalty) newList.removeLast());
1372                wrongBox = (KnuthBox) newList.removeLast();
1373            }
1374
1375            // if this paragraph is at the bottom of a page, the space after
1376
// must be ignored; compute the length change
1377
int decreasedLength = (neededUnits(totalLength.opt)
1378                                   - neededUnits(totalLength.opt - adjustedSpaceAfter))
1379                                  * bpUnit;
1380            // insert the correct box
1381
newList.addLast(new KnuthBox(wrongBox.getW() - decreasedLength,
1382                                         wrongBox.getPosition(), false));
1383            // add preserved elements
1384
if (preserveList.size() > 0) {
1385                newList.addAll(preserveList);
1386            }
1387            // insert the correct glue
1388
newList.addLast(new KnuthGlue(decreasedLength, 0, 0, SPACE_AFTER_ADJUSTMENT,
1389                                          wrongBox.getPosition(), false));
1390            //log.debug(" rimosso box " + neededUnits(wrongBox.getW()));
1391
//log.debug(" aggiunto box " + neededUnits(
1392
// wrongBox.getW() - decreasedLength));
1393
//log.debug(" aggiunto glue " + neededUnits(decreasedLength) + " 0 0");
1394
}
1395
1396        return newList;
1397    }
1398
1399    protected static class StackingIter extends PositionIterator {
1400        StackingIter(Iterator JavaDoc parentIter) {
1401            super(parentIter);
1402        }
1403    
1404        protected LayoutManager getLM(Object JavaDoc nextObj) {
1405            return ((Position) nextObj).getLM();
1406        }
1407    
1408        protected Position getPos(Object JavaDoc nextObj) {
1409            return ((Position) nextObj);
1410        }
1411    }
1412
1413    protected static class MappingPosition extends Position {
1414        private int iFirstIndex;
1415        private int iLastIndex;
1416    
1417        public MappingPosition(LayoutManager lm, int first, int last) {
1418            super(lm);
1419            iFirstIndex = first;
1420            iLastIndex = last;
1421        }
1422        
1423        public int getFirstIndex() {
1424            return iFirstIndex;
1425        }
1426        
1427        public int getLastIndex() {
1428            return iLastIndex;
1429        }
1430    }
1431
1432    /**
1433     * "wrap" the Position inside each element moving the elements from
1434     * SourceList to targetList
1435     * @param sourceList source list
1436     * @param targetList target list receiving the wrapped position elements
1437     */

1438    protected void wrapPositionElements(List JavaDoc sourceList, List JavaDoc targetList) {
1439        wrapPositionElements(sourceList, targetList, false);
1440    }
1441    
1442    /**
1443     * "wrap" the Position inside each element moving the elements from
1444     * SourceList to targetList
1445     * @param sourceList source list
1446     * @param targetList target list receiving the wrapped position elements
1447     * @param force if true, every Position is wrapped regardless of its LM of origin
1448     */

1449    protected void wrapPositionElements(List JavaDoc sourceList, List JavaDoc targetList, boolean force) {
1450          
1451        ListIterator JavaDoc listIter = sourceList.listIterator();
1452        while (listIter.hasNext()) {
1453            ListElement tempElement;
1454            tempElement = (ListElement) listIter.next();
1455            if (force || tempElement.getLayoutManager() != this) {
1456                tempElement.setPosition(notifyPos(new NonLeafPosition(this,
1457                        tempElement.getPosition())));
1458            }
1459            targetList.add(tempElement);
1460        }
1461    }
1462
1463    /** @return the sum of start-indent and end-indent */
1464    protected int getIPIndents() {
1465        return startIndent + endIndent;
1466    }
1467    
1468    /**
1469     * Returns the IPD of the content area
1470     * @return the IPD of the content area
1471     */

1472    public int getContentAreaIPD() {
1473        return contentAreaIPD;
1474    }
1475   
1476    /**
1477     * Sets the IPD of the content area
1478     * @param contentAreaIPD the IPD of the content area
1479     */

1480    protected void setContentAreaIPD(int contentAreaIPD) {
1481        this.contentAreaIPD = contentAreaIPD;
1482    }
1483    
1484    /**
1485     * Returns the BPD of the content area
1486     * @return the BPD of the content area
1487     */

1488    public int getContentAreaBPD() {
1489        return -1;
1490    }
1491    
1492}
1493
1494
Popular Tags