KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > layoutmgr > table > TableContentLayoutManager


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: TableContentLayoutManager.java 478928 2006-11-24 17:32:48Z vhennebert $ */
19
20 package org.apache.fop.layoutmgr.table;
21
22 import java.util.Arrays JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.LinkedList JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Map JavaDoc;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.fop.area.Block;
31 import org.apache.fop.area.Trait;
32 import org.apache.fop.datatypes.PercentBaseContext;
33 import org.apache.fop.fo.Constants;
34 import org.apache.fop.fo.FONode;
35 import org.apache.fop.fo.FObj;
36 import org.apache.fop.fo.flow.Table;
37 import org.apache.fop.fo.flow.TableBody;
38 import org.apache.fop.fo.flow.TableRow;
39 import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
40 import org.apache.fop.fo.properties.LengthRangeProperty;
41 import org.apache.fop.layoutmgr.BreakElement;
42 import org.apache.fop.layoutmgr.ElementListObserver;
43 import org.apache.fop.layoutmgr.ElementListUtils;
44 import org.apache.fop.layoutmgr.KnuthBox;
45 import org.apache.fop.layoutmgr.KnuthElement;
46 import org.apache.fop.layoutmgr.KnuthPenalty;
47 import org.apache.fop.layoutmgr.KnuthPossPosIter;
48 import org.apache.fop.layoutmgr.LayoutContext;
49 import org.apache.fop.layoutmgr.LayoutManager;
50 import org.apache.fop.layoutmgr.ListElement;
51 import org.apache.fop.layoutmgr.MinOptMaxUtil;
52 import org.apache.fop.layoutmgr.Position;
53 import org.apache.fop.layoutmgr.PositionIterator;
54 import org.apache.fop.layoutmgr.SpaceResolver;
55 import org.apache.fop.layoutmgr.TraitSetter;
56 import org.apache.fop.layoutmgr.SpaceResolver.SpaceHandlingBreakPosition;
57 import org.apache.fop.traits.MinOptMax;
58
59 /**
60  * Layout manager for table contents, particularly managing the creation of combined element lists.
61  */

62 public class TableContentLayoutManager implements PercentBaseContext {
63
64     /** Logger **/
65     private static Log log = LogFactory.getLog(TableContentLayoutManager.class);
66
67     private TableLayoutManager tableLM;
68     private TableRowIterator bodyIter;
69     private TableRowIterator headerIter;
70     private TableRowIterator footerIter;
71     private LinkedList JavaDoc headerList;
72     private LinkedList JavaDoc footerList;
73     private int headerNetHeight = 0;
74     private int footerNetHeight = 0;
75     private boolean firstBreakBeforeServed = false;
76
77     private int startXOffset;
78     private int usedBPD;
79     
80     private TableStepper stepper = new TableStepper(this);
81         
82     /**
83      * Main constructor
84      * @param parent Parent layout manager
85      */

86     public TableContentLayoutManager(TableLayoutManager parent) {
87         this.tableLM = parent;
88         Table table = getTableLM().getTable();
89         this.bodyIter = new TableRowIterator(table, getTableLM().getColumns(),
90                 TableRowIterator.BODY);
91         if (table.getTableHeader() != null) {
92             headerIter = new TableRowIterator(table,
93                     getTableLM().getColumns(), TableRowIterator.HEADER);
94         }
95         if (table.getTableFooter() != null) {
96             footerIter = new TableRowIterator(table,
97                     getTableLM().getColumns(), TableRowIterator.FOOTER);
98         }
99     }
100     
101     /**
102      * @return the table layout manager
103      */

104     public TableLayoutManager getTableLM() {
105         return this.tableLM;
106     }
107     
108     /** @return true if the table uses the separate border model. */
109     private boolean isSeparateBorderModel() {
110         return getTableLM().getTable().isSeparateBorderModel();
111     }
112     
113     /**
114      * @return the column setup of this table
115      */

116     public ColumnSetup getColumns() {
117         return getTableLM().getColumns();
118     }
119     
120     /** @return the net header height */
121     protected int getHeaderNetHeight() {
122         return this.headerNetHeight;
123     }
124
125     /** @return the net footer height */
126     protected int getFooterNetHeight() {
127         return this.footerNetHeight;
128     }
129
130     /** @return the header element list */
131     protected LinkedList JavaDoc getHeaderElements() {
132         return this.headerList;
133     }
134
135     /** @return the footer element list */
136     protected LinkedList JavaDoc getFooterElements() {
137         return this.footerList;
138     }
139
140     /** @see org.apache.fop.layoutmgr.LayoutManager */
141     public LinkedList JavaDoc getNextKnuthElements(LayoutContext context, int alignment) {
142         log.debug("==> Columns: " + getTableLM().getColumns());
143         KnuthBox headerAsFirst = null;
144         KnuthBox headerAsSecondToLast = null;
145         KnuthBox footerAsLast = null;
146         if (headerIter != null && headerList == null) {
147             this.headerList = getKnuthElementsForRowIterator(
148                     headerIter, context, alignment, TableRowIterator.HEADER);
149             ElementListUtils.removeLegalBreaks(this.headerList);
150             this.headerNetHeight
151                     = ElementListUtils.calcContentLength(this.headerList);
152             if (log.isDebugEnabled()) {
153                 log.debug("==> Header: "
154                         + headerNetHeight + " - " + this.headerList);
155             }
156             TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
157                     getTableLM(), true, this.headerList);
158             KnuthBox box = new KnuthBox(headerNetHeight, pos, false);
159             if (getTableLM().getTable().omitHeaderAtBreak()) {
160                 //We can simply add the table header at the start
161
//of the whole list
162
headerAsFirst = box;
163             } else {
164                 headerAsSecondToLast = box;
165             }
166         }
167         if (footerIter != null && footerList == null) {
168             this.footerList = getKnuthElementsForRowIterator(
169                     footerIter, context, alignment, TableRowIterator.FOOTER);
170             ElementListUtils.removeLegalBreaks(this.footerList);
171             this.footerNetHeight
172                     = ElementListUtils.calcContentLength(this.footerList);
173             if (log.isDebugEnabled()) {
174                 log.debug("==> Footer: "
175                         + footerNetHeight + " - " + this.footerList);
176             }
177             //We can simply add the table footer at the end of the whole list
178
TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
179                     getTableLM(), false, this.footerList);
180             KnuthBox box = new KnuthBox(footerNetHeight, pos, false);
181             footerAsLast = box;
182         }
183         LinkedList JavaDoc returnList = getKnuthElementsForRowIterator(
184                 bodyIter, context, alignment, TableRowIterator.BODY);
185         if (headerAsFirst != null) {
186             returnList.add(0, headerAsFirst);
187         } else if (headerAsSecondToLast != null) {
188             returnList.add(headerAsSecondToLast);
189         }
190         if (footerAsLast != null) {
191             returnList.add(footerAsLast);
192         }
193         return returnList;
194     }
195     
196     /**
197      * Creates Knuth elements by iterating over a TableRowIterator.
198      * @param iter TableRowIterator instance to fetch rows from
199      * @param context Active LayoutContext
200      * @param alignment alignment indicator
201      * @param bodyType Indicates what kind of body is being processed
202      * (BODY, HEADER or FOOTER)
203      * @return An element list
204      */

205     private LinkedList JavaDoc getKnuthElementsForRowIterator(TableRowIterator iter,
206             LayoutContext context, int alignment, int bodyType) {
207         LinkedList JavaDoc returnList = new LinkedList JavaDoc();
208         EffRow[] rowGroup = null;
209         while ((rowGroup = iter.getNextRowGroup()) != null) {
210             //Check for break-before on the table-row at the start of the row group
211
TableRow rowFO = rowGroup[0].getTableRow();
212             if (rowFO != null && rowFO.getBreakBefore() != Constants.EN_AUTO) {
213                 log.info("break-before found");
214                 if (returnList.size() > 0) {
215                     ListElement last = (ListElement)returnList.getLast();
216                     if (last.isPenalty()) {
217                         KnuthPenalty pen = (KnuthPenalty)last;
218                         pen.setP(-KnuthPenalty.INFINITE);
219                         pen.setBreakClass(rowFO.getBreakBefore());
220                     } else if (last instanceof BreakElement) {
221                         BreakElement breakPoss = (BreakElement) last;
222                         breakPoss.setPenaltyValue(-KnuthPenalty.INFINITE);
223                         breakPoss.setBreakClass(rowFO.getBreakBefore());
224                     }
225                 } else {
226                     if (!firstBreakBeforeServed) {
227                         returnList.add(new BreakElement(new Position(getTableLM()),
228                                 0, -KnuthPenalty.INFINITE, rowFO.getBreakBefore(), context));
229                         iter.backToPreviousRow();
230                         firstBreakBeforeServed = true;
231                         break;
232                     }
233                 }
234             }
235             firstBreakBeforeServed = true;
236             
237             //Border resolution
238
if (!isSeparateBorderModel()) {
239                 resolveNormalBeforeAfterBordersForRowGroup(rowGroup, iter);
240             }
241             
242             //Element list creation
243
createElementsForRowGroup(context, alignment, bodyType,
244                         returnList, rowGroup);
245             
246             //Handle keeps
247
if (context.isKeepWithNextPending()) {
248                 log.debug("child LM (row group) signals pending keep-with-next");
249             }
250             if (context.isKeepWithPreviousPending()) {
251                 log.debug("child LM (row group) signals pending keep-with-previous");
252                 if (returnList.size() > 0) {
253                     //Modify last penalty
254
ListElement last = (ListElement)returnList.getLast();
255                     if (last.isPenalty()) {
256                         BreakElement breakPoss = (BreakElement)last;
257                         //Only honor keep if there's no forced break
258
if (!breakPoss.isForcedBreak()) {
259                             breakPoss.setPenaltyValue(KnuthPenalty.INFINITE);
260                         }
261                     }
262                 }
263             }
264             
265             //Check for break-after on the table-row at the end of the row group
266
rowFO = rowGroup[rowGroup.length - 1].getTableRow();
267             if (rowFO != null && rowFO.getBreakAfter() != Constants.EN_AUTO) {
268                 if (returnList.size() > 0) {
269                     ListElement last = (ListElement)returnList.getLast();
270                     if (last instanceof KnuthPenalty) {
271                         KnuthPenalty pen = (KnuthPenalty)last;
272                         pen.setP(-KnuthPenalty.INFINITE);
273                         pen.setBreakClass(rowFO.getBreakAfter());
274                     } else if (last instanceof BreakElement) {
275                         BreakElement breakPoss = (BreakElement)last;
276                         breakPoss.setPenaltyValue(-KnuthPenalty.INFINITE);
277                         breakPoss.setBreakClass(rowFO.getBreakAfter());
278                     }
279                 }
280             }
281         }
282         
283         if (returnList.size() > 0) {
284             //Remove last penalty
285
ListElement last = (ListElement)returnList.getLast();
286             if (last.isPenalty() || last instanceof BreakElement) {
287                 if (!last.isForcedBreak()) {
288                     //Only remove if we don't signal a forced break
289
returnList.removeLast();
290                 }
291             }
292         }
293
294         //fox:widow-content-limit
295
int widowContentLimit = getTableLM().getTable().getWidowContentLimit().getValue();
296         if (widowContentLimit != 0 && bodyType == TableRowIterator.BODY) {
297             ElementListUtils.removeLegalBreaks(returnList, widowContentLimit);
298         }
299         //fox:orphan-content-limit
300
int orphanContentLimit = getTableLM().getTable().getOrphanContentLimit().getValue();
301         if (orphanContentLimit != 0 && bodyType == TableRowIterator.BODY) {
302             ElementListUtils.removeLegalBreaksFromEnd(returnList, orphanContentLimit);
303         }
304         
305         return returnList;
306     }
307
308     /**
309      * Resolves normal borders for a row group.
310      * @param iter Table row iterator to operate on
311      */

312     private void resolveNormalBeforeAfterBordersForRowGroup(EffRow[] rowGroup,
313             TableRowIterator iter) {
314         for (int rgi = 0; rgi < rowGroup.length; rgi++) {
315             EffRow row = rowGroup[rgi];
316             EffRow prevRow = iter.getPrecedingRow(row);
317             EffRow nextRow = iter.getFollowingRow(row);
318             if ((prevRow == null) && (iter == this.bodyIter) && (this.headerIter != null)) {
319                 prevRow = this.headerIter.getLastRow();
320             }
321             if ((nextRow == null) && (iter == this.headerIter)) {
322                 nextRow = this.bodyIter.getFirstRow();
323             }
324             if ((nextRow == null) && (iter == this.bodyIter) && (this.footerIter != null)) {
325                 nextRow = this.footerIter.getFirstRow();
326             }
327             if ((prevRow == null) && (iter == this.footerIter)) {
328                 //TODO This could be bad for memory consumption because it already causes the
329
//whole body iterator to be prefetched!
330
prevRow = this.bodyIter.getLastRow();
331             }
332             log.debug(prevRow + " - " + row + " - " + nextRow);
333             
334             //Determine the grid units necessary for getting all the borders right
335
int guCount = row.getGridUnits().size();
336             if (prevRow != null) {
337                 guCount = Math.max(guCount, prevRow.getGridUnits().size());
338             }
339             if (nextRow != null) {
340                 guCount = Math.max(guCount, nextRow.getGridUnits().size());
341             }
342             GridUnit gu = row.getGridUnit(0);
343             //Create empty grid units to hold resolved borders of neighbouring cells
344
//TODO maybe this needs to be done differently (and sooner)
345
for (int i = 0; i < guCount - row.getGridUnits().size(); i++) {
346                 //TODO This block is untested!
347
int pos = row.getGridUnits().size() + i;
348                 row.getGridUnits().add(new EmptyGridUnit(gu.getRow(),
349                         this.tableLM.getColumns().getColumn(pos + 1), gu.getBody(),
350                         pos));
351             }
352             
353             //Now resolve normal borders
354
if (getTableLM().getTable().isSeparateBorderModel()) {
355                 //nop, borders are already assigned at this point
356
} else {
357                 for (int i = 0; i < row.getGridUnits().size(); i++) {
358                     gu = row.getGridUnit(i);
359                     GridUnit other;
360                     int flags = 0;
361                     if (prevRow != null && i < prevRow.getGridUnits().size()) {
362                         other = prevRow.getGridUnit(i);
363                     } else {
364                         other = null;
365                     }
366                     if (other == null
367                             || other.isEmpty()
368                             || gu.isEmpty()
369                             || gu.getPrimary() != other.getPrimary()) {
370                         if ((iter == this.bodyIter)
371                                 && gu.getFlag(GridUnit.FIRST_IN_TABLE)
372                                 && (this.headerIter == null)) {
373                             flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
374                         }
375                         if ((iter == this.headerIter)
376                                 && gu.getFlag(GridUnit.FIRST_IN_TABLE)) {
377                             flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
378                         }
379                         gu.resolveBorder(other,
380                                 CommonBorderPaddingBackground.BEFORE, flags);
381                     }
382                     
383                     flags = 0;
384                     if (nextRow != null && i < nextRow.getGridUnits().size()) {
385                         other = nextRow.getGridUnit(i);
386                     } else {
387                         other = null;
388                     }
389                     if (other == null
390                             || other.isEmpty()
391                             || gu.isEmpty()
392                             || gu.getPrimary() != other.getPrimary()) {
393                         if ((iter == this.bodyIter)
394                                 && gu.getFlag(GridUnit.LAST_IN_TABLE)
395                                 && (this.footerIter == null)) {
396                             flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
397                         }
398                         if ((iter == this.footerIter)
399                                 && gu.getFlag(GridUnit.LAST_IN_TABLE)) {
400                             flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
401                         }
402                         gu.resolveBorder(other,
403                                 CommonBorderPaddingBackground.AFTER, flags);
404                     }
405                 }
406
407             }
408         }
409     }
410
411     /**
412      * Creates Knuth elements for a row group (see TableRowIterator.getNextRowGroup()).
413      * @param context Active LayoutContext
414      * @param alignment alignment indicator
415      * @param bodyType Indicates what kind of body is being processed (BODY, HEADER or FOOTER)
416      * @param returnList List to received the generated elements
417      * @param rowGroup row group to process
418      */

419     private void createElementsForRowGroup(LayoutContext context, int alignment,
420             int bodyType, LinkedList JavaDoc returnList,
421             EffRow[] rowGroup) {
422         log.debug("Handling row group with " + rowGroup.length + " rows...");
423         MinOptMax[] rowHeights = new MinOptMax[rowGroup.length];
424         MinOptMax[] explicitRowHeights = new MinOptMax[rowGroup.length];
425         EffRow row;
426         int maxColumnCount = 0;
427         List JavaDoc pgus = new java.util.ArrayList JavaDoc(); //holds a list of a row's primary grid units
428
for (int rgi = 0; rgi < rowGroup.length; rgi++) {
429             row = rowGroup[rgi];
430             rowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE);
431             explicitRowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE);
432             
433             pgus.clear();
434             TableRow tableRow = null;
435             int minContentHeight = 0;
436             int maxCellHeight = 0;
437             int effRowContentHeight = 0;
438             for (int j = 0; j < row.getGridUnits().size(); j++) {
439                 maxColumnCount = Math.max(maxColumnCount, row.getGridUnits().size());
440                 GridUnit gu = row.getGridUnit(j);
441                 if ((gu.isPrimary() || (gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan()))
442                         && !gu.isEmpty()) {
443                     PrimaryGridUnit primary = gu.getPrimary();
444                     
445                     if (gu.isPrimary()) {
446                         primary.getCellLM().setParent(getTableLM());
447                      
448                         //Determine the table-row if any
449
if (tableRow == null && primary.getRow() != null) {
450                             tableRow = primary.getRow();
451                             
452                             //Check for bpd on row, see CSS21, 17.5.3 Table height algorithms
453
LengthRangeProperty bpd = tableRow.getBlockProgressionDimension();
454                             if (!bpd.getMinimum(getTableLM()).isAuto()) {
455                                 minContentHeight = Math.max(
456                                         minContentHeight,
457                                         bpd.getMinimum(
458                                                 getTableLM()).getLength().getValue(getTableLM()));
459                             }
460                             MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd, getTableLM());
461                             
462                         }
463
464                         //Calculate width of cell
465
int spanWidth = 0;
466                         for (int i = primary.getStartCol();
467                                 i < primary.getStartCol()
468                                         + primary.getCell().getNumberColumnsSpanned();
469                                 i++) {
470                             if (getTableLM().getColumns().getColumn(i + 1) != null) {
471                                 spanWidth += getTableLM().getColumns().getColumn(i + 1)
472                                     .getColumnWidth().getValue(getTableLM());
473                             }
474                         }
475                         LayoutContext childLC = new LayoutContext(0);
476                         childLC.setStackLimit(context.getStackLimit()); //necessary?
477
childLC.setRefIPD(spanWidth);
478                         
479                         //Get the element list for the cell contents
480
LinkedList JavaDoc elems = primary.getCellLM().getNextKnuthElements(
481                                                 childLC, alignment);
482                         //Temporary? Multiple calls in case of break conditions.
483
//TODO Revisit when table layout is restartable
484
while (!primary.getCellLM().isFinished()) {
485                             LinkedList JavaDoc additionalElems = primary.getCellLM().getNextKnuthElements(
486                                     childLC, alignment);
487                             elems.addAll(additionalElems);
488                         }
489                         ElementListObserver.observe(elems, "table-cell", primary.getCell().getId());
490
491                         if ((elems.size() > 0)
492                                 && ((KnuthElement)elems.getLast()).isForcedBreak()) {
493                             // a descendant of this block has break-after
494
log.debug("Descendant of table-cell signals break: "
495                                     + primary.getCellLM().isFinished());
496                         }
497                         
498                         primary.setElements(elems);
499                         
500                         if (childLC.isKeepWithNextPending()) {
501                             log.debug("child LM signals pending keep-with-next");
502                             primary.setFlag(GridUnit.KEEP_WITH_NEXT_PENDING, true);
503                         }
504                         if (childLC.isKeepWithPreviousPending()) {
505                             log.debug("child LM signals pending keep-with-previous");
506                             primary.setFlag(GridUnit.KEEP_WITH_PREVIOUS_PENDING, true);
507                         }
508                     }
509
510                     
511                     //Calculate height of cell contents
512
primary.setContentLength(ElementListUtils.calcContentLength(
513                             primary.getElements()));
514                     maxCellHeight = Math.max(maxCellHeight, primary.getContentLength());
515
516                     //Calculate height of row, see CSS21, 17.5.3 Table height algorithms
517
if (gu.isLastGridUnitRowSpan()) {
518                         int effCellContentHeight = minContentHeight;
519                         LengthRangeProperty bpd = primary.getCell().getBlockProgressionDimension();
520                         if (!bpd.getMinimum(getTableLM()).isAuto()) {
521                             effCellContentHeight = Math.max(
522                                 effCellContentHeight,
523                                 bpd.getMinimum(getTableLM()).getLength().getValue(getTableLM()));
524                         }
525                         if (!bpd.getOptimum(getTableLM()).isAuto()) {
526                             effCellContentHeight = Math.max(
527                                 effCellContentHeight,
528                                 bpd.getOptimum(getTableLM()).getLength().getValue(getTableLM()));
529                         }
530                         if (gu.getRowSpanIndex() == 0) {
531                             //TODO ATM only non-row-spanned cells are taken for this
532
MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd, tableLM);
533                         }
534                         effCellContentHeight = Math.max(effCellContentHeight,
535                                 primary.getContentLength());
536                         
537                         int borderWidths;
538                         if (isSeparateBorderModel()) {
539                             borderWidths = primary.getBorders().getBorderBeforeWidth(false)
540                                     + primary.getBorders().getBorderAfterWidth(false);
541                         } else {
542                             borderWidths = primary.getHalfMaxBorderWidth();
543                         }
544                         int padding = 0;
545                         effRowContentHeight = Math.max(effRowContentHeight,
546                                 effCellContentHeight);
547                         CommonBorderPaddingBackground cbpb
548                             = primary.getCell().getCommonBorderPaddingBackground();
549                         padding += cbpb.getPaddingBefore(false, primary.getCellLM());
550                         padding += cbpb.getPaddingAfter(false, primary.getCellLM());
551                         int effRowHeight = effCellContentHeight
552                                 + padding + borderWidths
553                                 + 2 * getTableLM().getHalfBorderSeparationBPD();
554                         for (int previous = 0; previous < gu.getRowSpanIndex(); previous++) {
555                             effRowHeight -= rowHeights[rgi - previous - 1].opt;
556                         }
557                         if (effRowHeight > rowHeights[rgi].min) {
558                             //This is the new height of the (grid) row
559
MinOptMaxUtil.extendMinimum(rowHeights[rgi], effRowHeight, false);
560                         }
561                     }
562                     
563                     if (gu.isPrimary()) {
564                         pgus.add(primary);
565                     }
566                 }
567             }
568
569             row.setHeight(rowHeights[rgi]);
570             row.setExplicitHeight(explicitRowHeights[rgi]);
571             if (effRowContentHeight > row.getExplicitHeight().max) {
572                 log.warn(FONode.decorateWithContextInfo(
573                         "The contents of row " + (row.getIndex() + 1)
574                         + " are taller than they should be (there is a"
575                         + " block-progression-dimension or height constraint on the indicated row)."
576                         + " Due to its contents the row grows"
577                         + " to " + effRowContentHeight + " millipoints, but the row shouldn't get"
578                         + " any taller than " + row.getExplicitHeight() + " millipoints.",
579                         row.getTableRow()));
580             }
581         }
582         if (log.isDebugEnabled()) {
583             log.debug("rowGroup:");
584             for (int i = 0; i < rowHeights.length; i++) {
585                 log.debug(" height=" + rowHeights[i] + " explicit=" + explicitRowHeights[i]);
586             }
587         }
588         LinkedList JavaDoc returnedList = this.stepper.getCombinedKnuthElementsForRowGroup(
589                 context, rowGroup, maxColumnCount, bodyType);
590         if (returnedList != null) {
591             returnList.addAll(returnedList);
592         }
593         
594     }
595
596     /**
597      * Retuns the X offset of the given grid unit.
598      * @param gu the grid unit
599      * @return the requested X offset
600      */

601     protected int getXOffsetOfGridUnit(GridUnit gu) {
602         int col = gu.getStartCol();
603         return startXOffset + getTableLM().getColumns().getXOffset(col + 1, getTableLM());
604     }
605     
606     /**
607      * Adds the areas generated by this layout manager to the area tree.
608      * @param parentIter the position iterator
609      * @param layoutContext the layout context for adding areas
610      */

611     public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
612         this.usedBPD = 0;
613         RowPainter painter = new RowPainter(layoutContext);
614
615         List JavaDoc positions = new java.util.ArrayList JavaDoc();
616         List JavaDoc headerElements = null;
617         List JavaDoc footerElements = null;
618         int nestedPenaltyArea = 0;
619         Position firstPos = null;
620         Position lastPos = null;
621         Position lastCheckPos = null;
622         while (parentIter.hasNext()) {
623             Position pos = (Position)parentIter.next();
624             if (pos instanceof SpaceHandlingBreakPosition) {
625                 //This position has only been needed before addAreas was called, now we need the
626
//original one created by the layout manager.
627
pos = ((SpaceHandlingBreakPosition)pos).getOriginalBreakPosition();
628             }
629             if (pos == null) {
630                 continue;
631             }
632             if (firstPos == null) {
633                 firstPos = pos;
634             }
635             lastPos = pos;
636             if (pos.getIndex() >= 0) {
637                 lastCheckPos = pos;
638             }
639             if (pos instanceof TableHeaderFooterPosition) {
640                 TableHeaderFooterPosition thfpos = (TableHeaderFooterPosition)pos;
641                 //these positions need to be unpacked
642
if (thfpos.header) {
643                     //Positions for header will be added first
644
headerElements = thfpos.nestedElements;
645                 } else {
646                     //Positions for footers are simply added at the end
647
footerElements = thfpos.nestedElements;
648                 }
649             } else if (pos instanceof TableHFPenaltyPosition) {
650                 //ignore for now, see special handling below if break is at a penalty
651
//Only if the last position in this part/page us such a position it will be used
652
} else {
653                 //leave order as is for the rest
654
positions.add(pos);
655             }
656         }
657         if (lastPos instanceof TableHFPenaltyPosition) {
658             TableHFPenaltyPosition penaltyPos = (TableHFPenaltyPosition)lastPos;
659             log.debug("Break at penalty!");
660             if (penaltyPos.headerElements != null) {
661                 //Header positions for the penalty position are in the last element and need to
662
//be handled first before all other TableContentPositions
663
headerElements = penaltyPos.headerElements;
664             }
665             if (penaltyPos.footerElements != null) {
666                 footerElements = penaltyPos.footerElements;
667             }
668             nestedPenaltyArea = penaltyPos.nestedPenaltyLength;
669         }
670
671         Map JavaDoc markers = getTableLM().getTable().getMarkers();
672         if (markers != null) {
673             getTableLM().getCurrentPV().addMarkers(markers,
674                     true, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos));
675         }
676         
677         if (headerElements != null) {
678             //header positions for the last part are the second-to-last element and need to
679
//be handled first before all other TableContentPositions
680
PositionIterator nestedIter = new KnuthPossPosIter(headerElements);
681             iterateAndPaintPositions(nestedIter, painter);
682             painter.addAreasAndFlushRow(true);
683         }
684         
685         //Iterate over all steps
686
Iterator JavaDoc posIter = positions.iterator();
687         iterateAndPaintPositions(posIter, painter);
688         painter.addAreasAndFlushRow(true);
689
690         painter.notifyNestedPenaltyArea(nestedPenaltyArea);
691         if (footerElements != null) {
692             //Positions for footers are simply added at the end
693
PositionIterator nestedIter = new KnuthPossPosIter(footerElements);
694             iterateAndPaintPositions(nestedIter, painter);
695             painter.addAreasAndFlushRow(true);
696         }
697         
698         painter.notifyEndOfSequence();
699         this.usedBPD += painter.getAccumulatedBPD();
700
701         if (markers != null) {
702             getTableLM().getCurrentPV().addMarkers(markers,
703                     false, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos));
704         }
705     }
706     
707     private void iterateAndPaintPositions(Iterator JavaDoc iterator, RowPainter painter) {
708         List JavaDoc lst = new java.util.ArrayList JavaDoc();
709         boolean firstPos = false;
710         boolean lastPos = false;
711         TableBody body = null;
712         while (iterator.hasNext()) {
713             Position pos = (Position)iterator.next();
714             if (pos instanceof TableContentPosition) {
715                 TableContentPosition tcpos = (TableContentPosition)pos;
716                 lst.add(tcpos);
717                 GridUnitPart part = (GridUnitPart)tcpos.gridUnitParts.get(0);
718                 if (body == null) {
719                     body = part.pgu.getBody();
720                 }
721                 if (tcpos.getFlag(TableContentPosition.FIRST_IN_ROWGROUP)
722                         && tcpos.row.getFlag(EffRow.FIRST_IN_PART)) {
723                     firstPos = true;
724
725                 }
726                 if (tcpos.getFlag(TableContentPosition.LAST_IN_ROWGROUP)
727                         && tcpos.row.getFlag(EffRow.LAST_IN_PART)) {
728                     lastPos = true;
729                     getTableLM().getCurrentPV().addMarkers(body.getMarkers(),
730                             true, firstPos, lastPos);
731                     int size = lst.size();
732                     for (int i = 0; i < size; i++) {
733                         painter.handleTableContentPosition((TableContentPosition)lst.get(i));
734                     }
735                     getTableLM().getCurrentPV().addMarkers(body.getMarkers(),
736                             false, firstPos, lastPos);
737                     //reset
738
firstPos = false;
739                     lastPos = false;
740                     body = null;
741                     lst.clear();
742                 }
743             } else {
744                 if (log.isDebugEnabled()) {
745                     log.debug("Ignoring position: " + pos);
746                 }
747             }
748         }
749         if (body != null) {
750             getTableLM().getCurrentPV().addMarkers(body.getMarkers(),
751                     true, firstPos, lastPos);
752             int size = lst.size();
753             for (int i = 0; i < size; i++) {
754                 painter.handleTableContentPosition((TableContentPosition)lst.get(i));
755             }
756             getTableLM().getCurrentPV().addMarkers(body.getMarkers(),
757                     false, firstPos, lastPos);
758         }
759     }
760    
761     private class RowPainter {
762         
763         private TableRow rowFO = null;
764         private int colCount = getColumns().getColumnCount();
765         private int yoffset = 0;
766         private int accumulatedBPD = 0;
767         private EffRow lastRow = null;
768         private LayoutContext layoutContext;
769         private int lastRowHeight = 0;
770         private int[] firstRow = new int[3];
771         private Map JavaDoc[] rowOffsets = new Map JavaDoc[] {new java.util.HashMap JavaDoc(),
772                 new java.util.HashMap JavaDoc(), new java.util.HashMap JavaDoc()};
773
774         //These three variables are our buffer to recombine the individual steps into cells
775
private PrimaryGridUnit[] gridUnits = new PrimaryGridUnit[colCount];
776         private int[] start = new int[colCount];
777         private int[] end = new int[colCount];
778         private int[] partLength = new int[colCount];
779         
780         public RowPainter(LayoutContext layoutContext) {
781             this.layoutContext = layoutContext;
782             Arrays.fill(firstRow, -1);
783             Arrays.fill(end, -1);
784         }
785         
786         public int getAccumulatedBPD() {
787             return this.accumulatedBPD;
788         }
789         
790         public void notifyEndOfSequence() {
791             this.accumulatedBPD += lastRowHeight; //for last row
792
}
793         
794         public void notifyNestedPenaltyArea(int length) {
795             this.lastRowHeight += length;
796         }
797         
798         public void handleTableContentPosition(TableContentPosition tcpos) {
799             if (lastRow != tcpos.row && lastRow != null) {
800                 addAreasAndFlushRow(false);
801                 yoffset += lastRowHeight;
802                 this.accumulatedBPD += lastRowHeight;
803             }
804             if (log.isDebugEnabled()) {
805                 log.debug("===handleTableContentPosition(" + tcpos);
806             }
807             rowFO = tcpos.row.getTableRow();
808             lastRow = tcpos.row;
809             Iterator JavaDoc partIter = tcpos.gridUnitParts.iterator();
810             //Iterate over all grid units in the current step
811
while (partIter.hasNext()) {
812                 GridUnitPart gup = (GridUnitPart)partIter.next();
813                 if (log.isDebugEnabled()) {
814                     log.debug(">" + gup);
815                 }
816                 int colIndex = gup.pgu.getStartCol();
817                 if (gridUnits[colIndex] != gup.pgu) {
818                     if (gridUnits[colIndex] != null) {
819                         log.warn("Replacing GU in slot " + colIndex
820                                 + ". Some content may not be painted.");
821                     }
822                     gridUnits[colIndex] = gup.pgu;
823                     start[colIndex] = gup.start;
824                     end[colIndex] = gup.end;
825                 } else {
826                     if (gup.end < end[colIndex]) {
827                         throw new IllegalStateException JavaDoc("Internal Error: stepper problem");
828                     }
829                     end[colIndex] = gup.end;
830                 }
831             }
832         }
833         
834         public int addAreasAndFlushRow(boolean forcedFlush) {
835             int actualRowHeight = 0;
836             int readyCount = 0;
837             
838             int bt = lastRow.getBodyType();
839             if (log.isDebugEnabled()) {
840                 log.debug("Remembering yoffset for row " + lastRow.getIndex() + ": " + yoffset);
841             }
842             rowOffsets[bt].put(new Integer JavaDoc(lastRow.getIndex()), new Integer JavaDoc(yoffset));
843
844             for (int i = 0; i < gridUnits.length; i++) {
845                 if ((gridUnits[i] != null)
846                         && (forcedFlush || (end[i] == gridUnits[i].getElements().size() - 1))) {
847                     if (log.isTraceEnabled()) {
848                         log.trace("getting len for " + i + " "
849                                 + start[i] + "-" + end[i]);
850                     }
851                     readyCount++;
852                     int len = ElementListUtils.calcContentLength(
853                             gridUnits[i].getElements(), start[i], end[i]);
854                     partLength[i] = len;
855                     if (log.isTraceEnabled()) {
856                         log.trace("len of part: " + len);
857                     }
858
859                     if (start[i] == 0) {
860                         LengthRangeProperty bpd = gridUnits[i].getCell()
861                                 .getBlockProgressionDimension();
862                         if (!bpd.getMinimum(getTableLM()).isAuto()) {
863                             int min = bpd.getMinimum(getTableLM())
864                                         .getLength().getValue(getTableLM());
865                             if (min > 0) {
866                                 len = Math.max(len, bpd.getMinimum(getTableLM())
867                                         .getLength().getValue(getTableLM()));
868                             }
869                         }
870                         if (!bpd.getOptimum(getTableLM()).isAuto()) {
871                             int opt = bpd.getOptimum(getTableLM())
872                                         .getLength().getValue(getTableLM());
873                             if (opt > 0) {
874                                 len = Math.max(len, opt);
875                             }
876                         }
877                         if (gridUnits[i].getRow() != null) {
878                             bpd = gridUnits[i].getRow().getBlockProgressionDimension();
879                             if (!bpd.getMinimum(getTableLM()).isAuto()) {
880                                 int min = bpd.getMinimum(getTableLM()).getLength()
881                                             .getValue(getTableLM());
882                                 if (min > 0) {
883                                     len = Math.max(len, min);
884                                 }
885                             }
886                         }
887                     }
888                     
889                     // Add the padding if any
890
len += gridUnits[i].getBorders()
891                                     .getPaddingBefore(false, gridUnits[i].getCellLM());
892                     len += gridUnits[i].getBorders()
893                                     .getPaddingAfter(false, gridUnits[i].getCellLM());
894
895                     //Now add the borders to the contentLength
896
if (isSeparateBorderModel()) {
897                         len += gridUnits[i].getBorders().getBorderBeforeWidth(false);
898                         len += gridUnits[i].getBorders().getBorderAfterWidth(false);
899                     }
900                     int startRow = Math.max(gridUnits[i].getStartRow(), firstRow[bt]);
901                     Integer JavaDoc storedOffset = (Integer JavaDoc)rowOffsets[bt].get(new Integer JavaDoc(startRow));
902                     int effYOffset;
903                     if (storedOffset != null) {
904                         effYOffset = storedOffset.intValue();
905                     } else {
906                         effYOffset = yoffset;
907                     }
908                     len -= yoffset - effYOffset;
909                     actualRowHeight = Math.max(actualRowHeight, len);
910                 }
911             }
912             if (readyCount == 0) {
913                 return 0;
914             }
915             actualRowHeight += 2 * getTableLM().getHalfBorderSeparationBPD();
916             lastRowHeight = actualRowHeight;
917             
918             //Add areas for row
919
addRowBackgroundArea(rowFO, actualRowHeight, layoutContext.getRefIPD(), yoffset);
920             for (int i = 0; i < gridUnits.length; i++) {
921                 GridUnit currentGU = lastRow.safelyGetGridUnit(i);
922                 if ((gridUnits[i] != null)
923                         && (forcedFlush || ((end[i] == gridUnits[i].getElements().size() - 1))
924                                 && (currentGU == null || currentGU.isLastGridUnitRowSpan()))
925                     || (gridUnits[i] == null && currentGU != null)) {
926                     //the last line in the "if" above is to avoid a premature end of an
927
//row-spanned cell because no GridUnitParts are generated after a cell is
928
//finished with its content. currentGU can be null if there's no grid unit
929
//at this place in the current row (empty cell and no borders to process)
930
if (log.isDebugEnabled()) {
931                         log.debug((forcedFlush ? "FORCED " : "") + "flushing..." + i + " "
932                                 + start[i] + "-" + end[i]);
933                     }
934                     PrimaryGridUnit gu = gridUnits[i];
935                     if (gu == null
936                             && !currentGU.isEmpty()
937                             && currentGU.getColSpanIndex() == 0
938                             && currentGU.isLastGridUnitColSpan()
939                             && (forcedFlush || currentGU.isLastGridUnitRowSpan())) {
940                         gu = currentGU.getPrimary();
941                     }
942                     if (gu != null) {
943                         addAreasForCell(gu, start[i], end[i],
944                                 lastRow,
945                                 partLength[i], actualRowHeight);
946                         gridUnits[i] = null;
947                         start[i] = 0;
948                         end[i] = -1;
949                         partLength[i] = 0;
950                     }
951                 }
952             }
953             return actualRowHeight;
954         }
955
956         private void addAreasForCell(PrimaryGridUnit pgu, int startPos, int endPos,
957                 EffRow row, int contentHeight, int rowHeight) {
958             int bt = row.getBodyType();
959             if (firstRow[bt] < 0) {
960                 firstRow[bt] = row.getIndex();
961             }
962             //Determine the first row in this sequence
963
int startRow = Math.max(pgu.getStartRow(), firstRow[bt]);
964             //Determine y offset for the cell
965
Integer JavaDoc offset = (Integer JavaDoc)rowOffsets[bt].get(new Integer JavaDoc(startRow));
966             while (offset == null) {
967                 startRow--;
968                 offset = (Integer JavaDoc)rowOffsets[bt].get(new Integer JavaDoc(startRow));
969             }
970             int effYOffset = offset.intValue();
971             int effCellHeight = rowHeight;
972             effCellHeight += yoffset - effYOffset;
973             if (log.isDebugEnabled()) {
974                 log.debug("Creating area for cell:");
975                 log.debug(" current row: " + row.getIndex());
976                 log.debug(" start row: " + pgu.getStartRow() + " " + yoffset + " " + effYOffset);
977                 log.debug(" contentHeight: " + contentHeight + " rowHeight=" + rowHeight
978                         + " effCellHeight=" + effCellHeight);
979             }
980             TableCellLayoutManager cellLM = pgu.getCellLM();
981             cellLM.setXOffset(getXOffsetOfGridUnit(pgu));
982             cellLM.setYOffset(effYOffset);
983             cellLM.setContentHeight(contentHeight);
984             cellLM.setRowHeight(effCellHeight);
985             //cellLM.setRowHeight(row.getHeight().opt);
986
int prevBreak = ElementListUtils.determinePreviousBreak(pgu.getElements(), startPos);
987             if (endPos >= 0) {
988                 SpaceResolver.performConditionalsNotification(pgu.getElements(),
989                         startPos, endPos, prevBreak);
990             }
991             cellLM.addAreas(new KnuthPossPosIter(pgu.getElements(),
992                     startPos, endPos + 1), layoutContext);
993         }
994         
995     }
996
997     /**
998      * Get the area for a row for background.
999      * @param row the table-row object or null
1000     * @return the row area or null if there's no background to paint
1001     */

1002    public Block getRowArea(TableRow row) {
1003        if (row == null || !row.getCommonBorderPaddingBackground().hasBackground()) {
1004            return null;
1005        } else {
1006            Block block = new Block();
1007            block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
1008            block.setPositioning(Block.ABSOLUTE);
1009            return block;
1010        }
1011    }
1012
1013    /**
1014     * Adds the area for the row background if any.
1015     * @param row row for which to generate the background
1016     * @param bpd block-progression-dimension of the row
1017     * @param ipd inline-progression-dimension of the row
1018     * @param yoffset Y offset at which to paint
1019     */

1020    public void addRowBackgroundArea(TableRow row, int bpd, int ipd, int yoffset) {
1021        //Add row background if any
1022
Block rowBackground = getRowArea(row);
1023        if (rowBackground != null) {
1024            rowBackground.setBPD(bpd);
1025            rowBackground.setIPD(ipd);
1026            rowBackground.setXOffset(this.startXOffset);
1027            rowBackground.setYOffset(yoffset);
1028            getTableLM().addChildArea(rowBackground);
1029            TraitSetter.addBackground(rowBackground,
1030                    row.getCommonBorderPaddingBackground(), getTableLM());
1031        }
1032    }
1033    
1034    
1035    /**
1036     * Sets the overall starting x-offset. Used for proper placement of cells.
1037     * @param startXOffset starting x-offset (table's start-indent)
1038     */

1039    public void setStartXOffset(int startXOffset) {
1040        this.startXOffset = startXOffset;
1041    }
1042
1043    /**
1044     * @return the amount of block-progression-dimension used by the content
1045     */

1046    public int getUsedBPD() {
1047        return this.usedBPD;
1048    }
1049    
1050    /**
1051     * Represents a non-dividable part of a grid unit. Used by the table stepper.
1052     */

1053    protected static class GridUnitPart {
1054        
1055        /** Primary grid unit */
1056        protected PrimaryGridUnit pgu;
1057        /** Index of the starting element of this part */
1058        protected int start;
1059        /** Index of the ending element of this part */
1060        protected int end;
1061        
1062        /**
1063         * Creates a new GridUnitPart.
1064         * @param pgu Primary grid unit
1065         * @param start starting element
1066         * @param end ending element
1067         */

1068        protected GridUnitPart(PrimaryGridUnit pgu, int start, int end) {
1069            this.pgu = pgu;
1070            this.start = start;
1071            this.end = end;
1072        }
1073        
1074        /** @return true if this part is the first part of a cell */
1075        public boolean isFirstPart() {
1076            return (start == 0);
1077        }
1078        
1079        /** @return true if this part is the last part of a cell */
1080        public boolean isLastPart() {
1081            return (end >= 0 && end == pgu.getElements().size() - 1);
1082        }
1083        
1084        /** @see java.lang.Object#toString() */
1085        public String JavaDoc toString() {
1086            StringBuffer JavaDoc sb = new StringBuffer JavaDoc("Part: ");
1087            sb.append(start).append("-").append(end);
1088            sb.append(" [").append(isFirstPart() ? "F" : "-").append(isLastPart() ? "L" : "-");
1089            sb.append("] ").append(pgu);
1090            return sb.toString();
1091        }
1092        
1093    }
1094    
1095    /**
1096     * This class represents a Position specific to this layout manager. Used for normal content
1097     * cases.
1098     */

1099    public static class TableContentPosition extends Position {
1100
1101        /** The position is the first of the row group. */
1102        public static final int FIRST_IN_ROWGROUP = 1;
1103        /** The position is the last of the row group. */
1104        public static final int LAST_IN_ROWGROUP = 2;
1105        
1106        /** the list of GridUnitParts making up this position */
1107        protected List JavaDoc gridUnitParts;
1108        /** effective row this position belongs to */
1109        protected EffRow row;
1110        /** flags for the position */
1111        protected int flags;
1112        
1113        /**
1114         * Creates a new TableContentPosition.
1115         * @param lm applicable layout manager
1116         * @param gridUnitParts the list of GridUnitPart instances
1117         * @param row effective row this position belongs to
1118         */

1119        protected TableContentPosition(LayoutManager lm, List JavaDoc gridUnitParts,
1120                EffRow row) {
1121            super(lm);
1122            this.gridUnitParts = gridUnitParts;
1123            this.row = row;
1124        }
1125        
1126        /**
1127         * Returns a flag for this GridUnit.
1128         * @param which the requested flag
1129         * @return the value of the flag
1130         */

1131        public boolean getFlag(int which) {
1132            return (flags & (1 << which)) != 0;
1133        }
1134        
1135        /**
1136         * Sets a flag on a GridUnit.
1137         * @param which the flag to set
1138         * @param value the new value for the flag
1139         */

1140        public void setFlag(int which, boolean value) {
1141            if (value) {
1142                flags |= (1 << which); //set flag
1143
} else {
1144                flags &= ~(1 << which); //clear flag
1145
}
1146        }
1147        
1148        /** @see org.apache.fop.layoutmgr.Position#generatesAreas() */
1149        public boolean generatesAreas() {
1150            return true;
1151        }
1152
1153        /** @see java.lang.Object#toString() */
1154        public String JavaDoc toString() {
1155            StringBuffer JavaDoc sb = new StringBuffer JavaDoc("TableContentPosition:");
1156            sb.append(getIndex());
1157            sb.append("[");
1158            sb.append(row.getIndex()).append("/");
1159            sb.append(getFlag(FIRST_IN_ROWGROUP) ? "F" : "-");
1160            sb.append(getFlag(LAST_IN_ROWGROUP) ? "L" : "-").append("]");
1161            sb.append("(");
1162            sb.append(gridUnitParts);
1163            sb.append(")");
1164            return sb.toString();
1165        }
1166    }
1167    
1168    /**
1169     * This class represents a Position specific to this layout manager. Used for table
1170     * headers and footers at the beginning and end of a table.
1171     */

1172    public static class TableHeaderFooterPosition extends Position {
1173
1174        /** True indicates a position for a header, false for a footer. */
1175        protected boolean header;
1176        /** Element list representing the header/footer */
1177        protected List JavaDoc nestedElements;
1178        
1179        /**
1180         * Creates a new TableHeaderFooterPosition.
1181         * @param lm applicable layout manager
1182         * @param header True indicates a position for a header, false for a footer.
1183         * @param nestedElements Element list representing the header/footer
1184         */

1185        protected TableHeaderFooterPosition(LayoutManager lm,
1186                boolean header, List JavaDoc nestedElements) {
1187            super(lm);
1188            this.header = header;
1189            this.nestedElements = nestedElements;
1190        }
1191        
1192        /** @see java.lang.Object#toString() */
1193        public String JavaDoc toString() {
1194            StringBuffer JavaDoc sb = new StringBuffer JavaDoc("Table");
1195            sb.append(header ? "Header" : "Footer");
1196            sb.append("Position:");
1197            sb.append(getIndex()).append("(");
1198            sb.append(nestedElements);
1199            sb.append(")");
1200            return sb.toString();
1201        }
1202    }
1203
1204    /**
1205    * This class represents a Position specific to this layout manager. Used for table
1206    * headers and footers at breaks.
1207    */

1208    public static class TableHFPenaltyPosition extends Position {
1209        
1210        /** Element list for the header */
1211        protected List JavaDoc headerElements;
1212        /** Element list for the footer */
1213        protected List JavaDoc footerElements;
1214        /** Penalty length to be respected for nested content */
1215        protected int nestedPenaltyLength;
1216        
1217        /**
1218         * Creates a new TableHFPenaltyPosition
1219         * @param lm applicable layout manager
1220         */

1221        protected TableHFPenaltyPosition(LayoutManager lm) {
1222            super(lm);
1223        }
1224        
1225        /** @see java.lang.Object#toString() */
1226        public String JavaDoc toString() {
1227            StringBuffer JavaDoc sb = new StringBuffer JavaDoc("TableHFPenaltyPosition:");
1228            sb.append(getIndex()).append("(");
1229            sb.append("header:");
1230            sb.append(headerElements);
1231            sb.append(", footer:");
1232            sb.append(footerElements);
1233            sb.append(", inner penalty length:");
1234            sb.append(nestedPenaltyLength);
1235            sb.append(")");
1236            return sb.toString();
1237        }
1238    }
1239    
1240    // --------- Property Resolution related functions --------- //
1241

1242    /**
1243     * @see org.apache.fop.datatypes.PercentBaseContext#getBaseLength(int, FObj)
1244     */

1245    public int getBaseLength(int lengthBase, FObj fobj) {
1246        return tableLM.getBaseLength(lengthBase, fobj);
1247    }
1248
1249}
1250
Popular Tags