KickJava   Java API By Example, From Geeks To Geeks.

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


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: TableLayoutManager.java 478928 2006-11-24 17:32:48Z vhennebert $ */
19
20 package org.apache.fop.layoutmgr.table;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.fop.fo.flow.Table;
25 import org.apache.fop.fo.flow.TableColumn;
26 import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
27 import org.apache.fop.layoutmgr.ConditionalElementListener;
28 import org.apache.fop.layoutmgr.KnuthElement;
29 import org.apache.fop.layoutmgr.KnuthGlue;
30 import org.apache.fop.layoutmgr.LayoutContext;
31 import org.apache.fop.layoutmgr.ListElement;
32 import org.apache.fop.layoutmgr.PositionIterator;
33 import org.apache.fop.layoutmgr.Position;
34 import org.apache.fop.layoutmgr.RelSide;
35 import org.apache.fop.layoutmgr.TraitSetter;
36 import org.apache.fop.area.Area;
37 import org.apache.fop.area.Block;
38 import org.apache.fop.traits.MinOptMax;
39 import org.apache.fop.traits.SpaceVal;
40
41 import java.util.Iterator JavaDoc;
42 import java.util.LinkedList JavaDoc;
43 import org.apache.fop.datatypes.LengthBase;
44 import org.apache.fop.fo.FONode;
45 import org.apache.fop.fo.FObj;
46
47 /**
48  * LayoutManager for a table FO.
49  * A table consists of columns, table header, table footer and multiple
50  * table bodies.
51  * The header, footer and body add the areas created from the table cells.
52  * The table then creates areas for the columns, bodies and rows
53  * the render background.
54  */

55 public class TableLayoutManager extends BlockStackingLayoutManager
56                 implements ConditionalElementListener {
57
58     /**
59      * logging instance
60      */

61     private static Log log = LogFactory.getLog(TableLayoutManager.class);
62     
63     private TableContentLayoutManager contentLM;
64     private ColumnSetup columns = null;
65
66     private Block curBlockArea;
67
68     private double tableUnit;
69     private boolean autoLayout = true;
70
71     private boolean discardBorderBefore;
72     private boolean discardBorderAfter;
73     private boolean discardPaddingBefore;
74     private boolean discardPaddingAfter;
75     private MinOptMax effSpaceBefore;
76     private MinOptMax effSpaceAfter;
77     
78     private int halfBorderSeparationBPD;
79     private int halfBorderSeparationIPD;
80     
81     /**
82      * Create a new table layout manager.
83      * @param node the table FO
84      */

85     public TableLayoutManager(Table node) {
86         super(node);
87         this.columns = new ColumnSetup(node);
88     }
89
90     /** @return the table FO */
91     public Table getTable() {
92         return (Table)this.fobj;
93     }
94     
95     /**
96      * @return the column setup for this table.
97      */

98     public ColumnSetup getColumns() {
99         return this.columns;
100     }
101     
102     /** @see org.apache.fop.layoutmgr.LayoutManager#initialize() */
103     public void initialize() {
104         foSpaceBefore = new SpaceVal(
105                 getTable().getCommonMarginBlock().spaceBefore, this).getSpace();
106         foSpaceAfter = new SpaceVal(
107                 getTable().getCommonMarginBlock().spaceAfter, this).getSpace();
108         startIndent = getTable().getCommonMarginBlock().startIndent.getValue(this);
109         endIndent = getTable().getCommonMarginBlock().endIndent.getValue(this);
110         
111         if (getTable().isSeparateBorderModel()) {
112             this.halfBorderSeparationBPD = getTable().getBorderSeparation().getBPD().getLength()
113                     .getValue(this) / 2;
114             this.halfBorderSeparationIPD = getTable().getBorderSeparation().getIPD().getLength()
115                     .getValue(this) / 2;
116         } else {
117             this.halfBorderSeparationBPD = 0;
118             this.halfBorderSeparationIPD = 0;
119         }
120         
121         if (!getTable().isAutoLayout()
122                 && getTable().getInlineProgressionDimension().getOptimum(this).getEnum()
123                     != EN_AUTO) {
124             autoLayout = false;
125         }
126     }
127
128     private void resetSpaces() {
129         this.discardBorderBefore = false;
130         this.discardBorderAfter = false;
131         this.discardPaddingBefore = false;
132         this.discardPaddingAfter = false;
133         this.effSpaceBefore = null;
134         this.effSpaceAfter = null;
135     }
136     
137     /** @return half the value of border-separation.block-progression-dimension. */
138     public int getHalfBorderSeparationBPD() {
139         return halfBorderSeparationBPD;
140     }
141
142     /** @return half the value of border-separation.inline-progression-dimension. */
143     public int getHalfBorderSeparationIPD() {
144         return halfBorderSeparationIPD;
145     }
146     
147     /** @see org.apache.fop.layoutmgr.LayoutManager */
148     public LinkedList JavaDoc getNextKnuthElements(LayoutContext context, int alignment) {
149         
150         LinkedList JavaDoc returnList = new LinkedList JavaDoc();
151         
152         if (!breakBeforeServed) {
153             try {
154                 if (addKnuthElementsForBreakBefore(returnList, context)) {
155                     return returnList;
156                 }
157             } finally {
158                 breakBeforeServed = true;
159             }
160         }
161
162         referenceIPD = context.getRefIPD();
163
164         if (getTable().getInlineProgressionDimension().getOptimum(this).getEnum() != EN_AUTO) {
165             int contentIPD = getTable().getInlineProgressionDimension().getOptimum(this)
166                     .getLength().getValue(this);
167             updateContentAreaIPDwithOverconstrainedAdjust(contentIPD);
168         } else {
169             if (!getTable().isAutoLayout()) {
170                 log.info("table-layout=\"fixed\" and width=\"auto\", "
171                         + "but auto-layout not supported "
172                         + "=> assuming width=\"100%\"");
173             }
174             updateContentAreaIPDwithOverconstrainedAdjust();
175         }
176
177         int sumOfColumns = columns.getSumOfColumnWidths(this);
178         if (!autoLayout && sumOfColumns > getContentAreaIPD()) {
179             log.debug(FONode.decorateWithContextInfo(
180                     "The sum of all column widths is larger than the specified table width.",
181                     getTable()));
182             updateContentAreaIPDwithOverconstrainedAdjust(sumOfColumns);
183         }
184
185         int availableIPD = referenceIPD - getIPIndents();
186         if (getContentAreaIPD() > availableIPD) {
187             log.warn(FONode.decorateWithContextInfo(
188                     "The extent in inline-progression-direction (width) of a table is"
189                     + " bigger than the available space ("
190                     + getContentAreaIPD() + "mpt > " + context.getRefIPD() + "mpt)",
191                     getTable()));
192         }
193         
194         /* initialize unit to determine computed values
195          * for proportional-column-width()
196          */

197         if (tableUnit == 0.0) {
198             this.tableUnit = columns.computeTableUnit(this);
199         }
200
201         if (!firstVisibleMarkServed) {
202             addKnuthElementsForSpaceBefore(returnList, alignment);
203         }
204         
205         if (getTable().isSeparateBorderModel()) {
206             addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
207             firstVisibleMarkServed = true;
208         }
209
210         //Spaces, border and padding to be repeated at each break
211
addPendingMarks(context);
212
213         LinkedList JavaDoc contentKnuthElements = null;
214         LinkedList JavaDoc contentList = new LinkedList JavaDoc();
215         //Position returnPosition = new NonLeafPosition(this, null);
216
//Body prevLM = null;
217

218         LayoutContext childLC = new LayoutContext(0);
219         /*
220         childLC.setStackLimit(
221               MinOptMax.subtract(context.getStackLimit(),
222                                  stackSize));*/

223         childLC.setRefIPD(context.getRefIPD());
224         childLC.copyPendingMarksFrom(context);
225
226         if (contentLM == null) {
227             contentLM = new TableContentLayoutManager(this);
228         }
229         contentKnuthElements = contentLM.getNextKnuthElements(childLC, alignment);
230         if (childLC.isKeepWithNextPending()) {
231             log.debug("TableContentLM signals pending keep-with-next");
232             context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING);
233         }
234         if (childLC.isKeepWithPreviousPending()) {
235             log.debug("TableContentLM signals pending keep-with-previous");
236             context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
237         }
238         
239         //Set index values on elements coming from the content LM
240
Iterator iter = contentKnuthElements.iterator();
241         while (iter.hasNext()) {
242             ListElement el = (ListElement)iter.next();
243             notifyPos(el.getPosition());
244         }
245         log.debug(contentKnuthElements);
246         
247         if (contentKnuthElements.size() == 1
248                 && ((ListElement)contentKnuthElements.getFirst()).isForcedBreak()) {
249             // a descendant of this block has break-before
250
if (returnList.size() == 0) {
251                 // the first child (or its first child ...) has
252
// break-before;
253
// all this block, including space before, will be put in
254
// the
255
// following page
256
//FIX ME
257
//bSpaceBeforeServed = false;
258
}
259             contentList.addAll(contentKnuthElements);
260
261             // "wrap" the Position inside each element
262
// moving the elements from contentList to returnList
263
contentKnuthElements = new LinkedList JavaDoc();
264             wrapPositionElements(contentList, returnList);
265
266             return returnList;
267         } else {
268             /*
269             if (prevLM != null) {
270                 // there is a block handled by prevLM
271                 // before the one handled by curLM
272                 if (mustKeepTogether()
273                         || prevLM.mustKeepWithNext()
274                         || curLM.mustKeepWithPrevious()) {
275                     // add an infinite penalty to forbid a break between
276                     // blocks
277                     contentList.add(new KnuthPenalty(0,
278                             KnuthElement.INFINITE, false,
279                             new Position(this), false));
280                 } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
281                     // add a null penalty to allow a break between blocks
282                     contentList.add(new KnuthPenalty(0, 0, false,
283                             new Position(this), false));
284                 } else {
285                     // the last element in contentList is a glue;
286                     // it is a feasible breakpoint, there is no need to add
287                     // a penalty
288                 }
289             }*/

290             contentList.addAll(contentKnuthElements);
291             if (contentKnuthElements.size() > 0) {
292                 if (((ListElement)contentKnuthElements.getLast()).isForcedBreak()) {
293                     // a descendant of this block has break-after
294
if (false /*curLM.isFinished()*/) {
295                         // there is no other content in this block;
296
// it's useless to add space after before a page break
297
setFinished(true);
298                     }
299
300                     contentKnuthElements = new LinkedList JavaDoc();
301                     wrapPositionElements(contentList, returnList);
302
303                     return returnList;
304                 }
305             }
306         }
307         wrapPositionElements(contentList, returnList);
308         if (getTable().isSeparateBorderModel()) {
309             addKnuthElementsForBorderPaddingAfter(returnList, true);
310         }
311         addKnuthElementsForSpaceAfter(returnList, alignment);
312         addKnuthElementsForBreakAfter(returnList, context);
313         setFinished(true);
314         resetSpaces();
315         return returnList;
316     }
317     
318     /**
319      * The table area is a reference area that contains areas for
320      * columns, bodies, rows and the contents are in cells.
321      *
322      * @param parentIter the position iterator
323      * @param layoutContext the layout context for adding areas
324      */

325     public void addAreas(PositionIterator parentIter,
326                          LayoutContext layoutContext) {
327         getParentArea(null);
328         getPSLM().addIDToPage(getTable().getId());
329
330         // add space before, in order to implement display-align = "center" or "after"
331
if (layoutContext.getSpaceBefore() != 0) {
332             addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
333         }
334
335         int startXOffset = getTable().getCommonMarginBlock().startIndent.getValue(this);
336         
337         // add column, body then row areas
338

339         int tableHeight = 0;
340         //Body childLM;
341
LayoutContext lc = new LayoutContext(0);
342
343
344         lc.setRefIPD(getContentAreaIPD());
345         contentLM.setStartXOffset(startXOffset);
346         contentLM.addAreas(parentIter, lc);
347         tableHeight += contentLM.getUsedBPD();
348
349         curBlockArea.setBPD(tableHeight);
350
351         if (getTable().isSeparateBorderModel()) {
352             TraitSetter.addBorders(curBlockArea,
353                     getTable().getCommonBorderPaddingBackground(),
354                     discardBorderBefore, discardBorderAfter, false, false, this);
355             TraitSetter.addPadding(curBlockArea,
356                     getTable().getCommonBorderPaddingBackground(),
357                     discardPaddingBefore, discardPaddingAfter, false, false, this);
358         }
359         TraitSetter.addBackground(curBlockArea,
360                 getTable().getCommonBorderPaddingBackground(),
361                 this);
362         TraitSetter.addMargins(curBlockArea,
363                 getTable().getCommonBorderPaddingBackground(),
364                 startIndent, endIndent,
365                 this);
366         TraitSetter.addBreaks(curBlockArea,
367                 getTable().getBreakBefore(), getTable().getBreakAfter());
368         TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(),
369                 effSpaceBefore, effSpaceAfter);
370
371         flush();
372
373         resetSpaces();
374         curBlockArea = null;
375         
376         getPSLM().notifyEndOfLayout(((Table)getFObj()).getId());
377     }
378
379     /**
380      * Return an Area which can contain the passed childArea. The childArea
381      * may not yet have any content, but it has essential traits set.
382      * In general, if the LayoutManager already has an Area it simply returns
383      * it. Otherwise, it makes a new Area of the appropriate class.
384      * It gets a parent area for its area by calling its parent LM.
385      * Finally, based on the dimensions of the parent area, it initializes
386      * its own area. This includes setting the content IPD and the maximum
387      * BPD.
388      *
389      * @param childArea the child area
390      * @return the parent area of the child
391      */

392     public Area getParentArea(Area childArea) {
393         if (curBlockArea == null) {
394             curBlockArea = new Block();
395             // Set up dimensions
396
// Must get dimensions from parent area
397
/*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
398             
399             TraitSetter.setProducerID(curBlockArea, getTable().getId());
400
401             curBlockArea.setIPD(getContentAreaIPD());
402             
403             setCurrentArea(curBlockArea);
404         }
405         return curBlockArea;
406     }
407
408     /**
409      * Add the child area to this layout manager.
410      *
411      * @param childArea the child area to add
412      */

413     public void addChildArea(Area childArea) {
414         if (curBlockArea != null) {
415             curBlockArea.addBlock((Block) childArea);
416         }
417     }
418
419     /**
420      * Reset the position of this layout manager.
421      *
422      * @param resetPos the position to reset to
423      */

424     public void resetPosition(Position resetPos) {
425         if (resetPos == null) {
426             reset(null);
427         }
428     }
429
430     /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */
431     public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
432         // TODO Auto-generated method stub
433
return 0;
434     }
435
436     /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */
437     public void discardSpace(KnuthGlue spaceGlue) {
438         // TODO Auto-generated method stub
439

440     }
441
442     /**
443      * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
444      */

445     public boolean mustKeepTogether() {
446         //TODO Keeps will have to be more sophisticated sooner or later
447
return super.mustKeepTogether()
448                 || !getTable().getKeepTogether().getWithinPage().isAuto()
449                 || !getTable().getKeepTogether().getWithinColumn().isAuto();
450     }
451
452     /**
453      * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
454      */

455     public boolean mustKeepWithPrevious() {
456         return !getTable().getKeepWithPrevious().getWithinPage().isAuto()
457                 || !getTable().getKeepWithPrevious().getWithinColumn().isAuto();
458     }
459
460     /**
461      * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
462      */

463     public boolean mustKeepWithNext() {
464         return !getTable().getKeepWithNext().getWithinPage().isAuto()
465                 || !getTable().getKeepWithNext().getWithinColumn().isAuto();
466     }
467
468     // --------- Property Resolution related functions --------- //
469

470     /**
471      * @see org.apache.fop.datatypes.PercentBaseContext#getBaseLength(int, FObj)
472      */

473     public int getBaseLength(int lengthBase, FObj fobj) {
474         // Special handler for TableColumn width specifications
475
if (fobj instanceof TableColumn && fobj.getParent() == getFObj()) {
476             switch (lengthBase) {
477             case LengthBase.CONTAINING_BLOCK_WIDTH:
478                 return getContentAreaIPD();
479             case LengthBase.TABLE_UNITS:
480                 return (int) this.tableUnit;
481             default:
482                 log.error("Unknown base type for LengthBase.");
483                 return 0;
484             }
485         } else {
486             switch (lengthBase) {
487             case LengthBase.TABLE_UNITS:
488                 return (int) this.tableUnit;
489             default:
490                 return super.getBaseLength(lengthBase, fobj);
491             }
492         }
493     }
494     
495     /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
496     public void notifySpace(RelSide side, MinOptMax effectiveLength) {
497         if (RelSide.BEFORE == side) {
498             if (log.isDebugEnabled()) {
499                 log.debug(this + ": Space " + side + ", "
500                         + this.effSpaceBefore + "-> " + effectiveLength);
501             }
502             this.effSpaceBefore = effectiveLength;
503         } else {
504             if (log.isDebugEnabled()) {
505                 log.debug(this + ": Space " + side + ", "
506                         + this.effSpaceAfter + "-> " + effectiveLength);
507             }
508             this.effSpaceAfter = effectiveLength;
509         }
510     }
511
512     /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
513     public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
514         if (effectiveLength == null) {
515             if (RelSide.BEFORE == side) {
516                 this.discardBorderBefore = true;
517             } else {
518                 this.discardBorderAfter = true;
519             }
520         }
521         if (log.isDebugEnabled()) {
522             log.debug(this + ": Border " + side + " -> " + effectiveLength);
523         }
524     }
525
526     /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
527     public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
528         if (effectiveLength == null) {
529             if (RelSide.BEFORE == side) {
530                 this.discardPaddingBefore = true;
531             } else {
532                 this.discardPaddingAfter = true;
533             }
534         }
535         if (log.isDebugEnabled()) {
536             log.debug(this + ": Padding " + side + " -> " + effectiveLength);
537         }
538     }
539
540 }
Popular Tags