KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > fo > flow > TableRow


1 /*
2  * $Id: TableRow.java,v 1.51.2.11 2003/04/11 00:24:39 pietsch Exp $
3  * ============================================================================
4  * The Apache Software License, Version 1.1
5  * ============================================================================
6  *
7  * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without modifica-
10  * tion, are permitted provided that the following conditions are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * 3. The end-user documentation included with the redistribution, if any, must
20  * include the following acknowledgment: "This product includes software
21  * developed by the Apache Software Foundation (http://www.apache.org/)."
22  * Alternately, this acknowledgment may appear in the software itself, if
23  * and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. The names "FOP" and "Apache Software Foundation" must not be used to
26  * endorse or promote products derived from this software without prior
27  * written permission. For written permission, please contact
28  * apache@apache.org.
29  *
30  * 5. Products derived from this software may not be called "Apache", nor may
31  * "Apache" appear in their name, without prior written permission of the
32  * Apache Software Foundation.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
35  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
37  * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
39  * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
40  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
41  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
43  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44  * ============================================================================
45  *
46  * This software consists of voluntary contributions made by many individuals
47  * on behalf of the Apache Software Foundation and was originally created by
48  * James Tauber <jtauber@jtauber.com>. For more information on the Apache
49  * Software Foundation, please see <http://www.apache.org/>.
50  */

51 package org.apache.fop.fo.flow;
52
53 // FOP
54
import org.apache.fop.fo.*;
55 import org.apache.fop.fo.properties.*;
56 import org.apache.fop.datatypes.*;
57 import org.apache.fop.layout.*;
58 import org.apache.fop.apps.FOPException;
59
60 // Java
61
import java.util.ArrayList JavaDoc;
62
63 public class TableRow extends FObj {
64
65     public static class Maker extends FObj.Maker {
66         public FObj make(FObj parent, PropertyList propertyList,
67                         String JavaDoc systemId, int line, int column)
68             throws FOPException {
69             return new TableRow(parent, propertyList, systemId, line, column);
70         }
71
72     }
73
74     public static FObj.Maker maker() {
75         return new TableRow.Maker();
76     }
77
78     boolean setup = false;
79
80     int breakAfter;
81     String JavaDoc id;
82
83     KeepValue keepWithNext;
84     KeepValue keepWithPrevious;
85     KeepValue keepTogether;
86
87     int widthOfCellsSoFar = 0;
88     int largestCellHeight = 0;
89     int minHeight = 0; // force row height
90
ArrayList JavaDoc columns;
91
92     AreaContainer areaContainer;
93
94     boolean areaAdded = false;
95     
96     boolean bIgnoreKeepTogether = false;
97     
98     private RowSpanMgr rowSpanMgr = null;
99     private CellArray cellArray = null;
100
101     private static class CellArray {
102         public static final byte EMPTY = 0;
103         public static final byte CELLSTART = 1;
104         public static final byte CELLSPAN = 2;
105
106         private TableCell[] cells;
107         private byte[] states;
108
109         public CellArray(RowSpanMgr rsi, int numColumns) {
110             // Initialize the cell array by marking any cell positions
111
// occupied by spans from previous rows
112
cells = new TableCell[numColumns];
113             states = new byte[numColumns];
114             for (int i = 0; i < numColumns; i++) {
115                 if (rsi.isSpanned(i + 1)) {
116                     cells[i] = rsi.getSpanningCell(i + 1);
117                     states[i] = CELLSPAN;
118                 } else
119                     states[i] = EMPTY;
120             }
121         }
122
123         /**
124          * Return column which doesn't already contain a span or a cell
125          * If past the end or no free cells after colNum, return -1
126          * Otherwise return value >= input value.
127          */

128         int getNextFreeCell(int colNum) {
129             for (int i = colNum - 1; i < states.length; i++) {
130                 if (states[i] == EMPTY)
131                     return i + 1;
132             }
133             return -1;
134         }
135
136
137         /**
138          * Return type of cell in colNum (1 based)
139          */

140         int getCellType(int colNum) {
141             if (colNum > 0 && colNum <= cells.length) {
142                 return states[colNum - 1];
143             } else
144                 return -1; // probably should throw exception
145
}
146
147         /**
148          * Return cell in colNum (1 based)
149          */

150         TableCell getCell(int colNum) {
151             if (colNum > 0 && colNum <= cells.length) {
152                 return cells[colNum - 1];
153             } else
154                 return null; // probably should throw exception
155
}
156
157         /**
158          * Store cell starting at cellColNum (1 based) and spanning numCols
159          * If any of the columns is already occupied, return false, else true
160          */

161         boolean storeCell(TableCell cell, int colNum, int numCols) {
162             boolean rslt = true;
163             int index = colNum - 1;
164             for (int count = 0; index < cells.length && count < numCols;
165                     count++, index++) {
166                 if (cells[index] == null) {
167                     cells[index] = cell;
168                     states[index] = (count == 0) ? CELLSTART : CELLSPAN;
169                 } else {
170                     rslt = false;
171                     // print a message but continue!!!
172
}
173             }
174             return rslt;
175         }
176     }
177
178
179     public TableRow(FObj parent, PropertyList propertyList,
180                     String JavaDoc systemId, int line, int column)
181         throws FOPException {
182         super(parent, propertyList, systemId, line, column);
183         if (!(parent instanceof AbstractTableBody)) {
184             throw new FOPException("A table row must be child of fo:table-body,"
185                                    + " fo:table-header or fo:table-footer, not "
186                                    + parent.getName(), systemId, line, column);
187         }
188     }
189
190     public String JavaDoc getName() {
191         return "fo:table-row";
192     }
193
194     public void setColumns(ArrayList JavaDoc columns) {
195         this.columns = columns;
196     }
197
198     public KeepValue getKeepWithPrevious() {
199         return keepWithPrevious;
200     }
201
202     public void doSetup(Area area) throws FOPException {
203
204         // Common Accessibility Properties
205
AccessibilityProps mAccProps = propMgr.getAccessibilityProps();
206             
207         // this.properties.get("block-progression-dimension");
208

209         // Common Aural Properties
210
AuralProps mAurProps = propMgr.getAuralProps();
211
212         // Common Border, Padding, and Background Properties
213
// only background apply, border apply if border-collapse
214
// is collapse.
215
BorderAndPadding bap = propMgr.getBorderAndPadding();
216         BackgroundProps bProps = propMgr.getBackgroundProps();
217
218         // Common Relative Position Properties
219
RelativePositionProps mRelProps = propMgr.getRelativePositionProps();
220                     
221         // this.properties.get("break-before");
222
// this.properties.get("break-after");
223
// this.properties.get("id");
224
// this.properties.get("height");
225
// this.properties.get("keep-together");
226
// this.properties.get("keep-with-next");
227
// this.properties.get("keep-with-previous");
228

229
230         this.breakAfter = this.properties.get("break-after").getEnum();
231
232         this.keepTogether = getKeepValue("keep-together.within-column");
233         this.keepWithNext = getKeepValue("keep-with-next.within-column");
234         this.keepWithPrevious =
235             getKeepValue("keep-with-previous.within-column");
236
237         this.id = this.properties.get("id").getString();
238         this.minHeight = this.properties.get("height").getLength().mvalue();
239         setup = true;
240     }
241
242     private KeepValue getKeepValue(String JavaDoc sPropName) {
243         Property p = this.properties.get(sPropName);
244         Number JavaDoc n = p.getNumber();
245         if (n != null)
246             return new KeepValue(KeepValue.KEEP_WITH_VALUE, n.intValue());
247         switch (p.getEnum()) {
248         case Constants.ALWAYS:
249             return new KeepValue(KeepValue.KEEP_WITH_ALWAYS, 0);
250         // break;
251
case Constants.AUTO:
252         default:
253             return new KeepValue(KeepValue.KEEP_WITH_AUTO, 0);
254         // break;
255
}
256     }
257
258     public int layout(Area area) throws FOPException {
259         if (this.marker == BREAK_AFTER) {
260             return Status.OK;
261         }
262
263         // Layout the first area for this FO
264
if (this.marker == START) {
265             if (!setup)
266                 doSetup(area);
267
268                 // Only do this once. If the row is "thrown" and we start
269
// layout over again, we can skip this.
270
if (cellArray == null) {
271                 initCellArray();
272                 // check to make sure this row hasn't been partially
273
// laid out yet (with an id created already)
274
}
275             // create ID also in case the row has been reset
276
try {
277                 area.getIDReferences().createID(id);
278             }
279             catch(FOPException e) {
280                 if (!e.isLocationSet()) {
281                     e.setLocation(systemId, line, column);
282                 }
283                 throw e;
284             }
285
286             this.marker = 0;
287             int breakStatus = propMgr.checkBreakBefore(area);
288             if (breakStatus != Status.OK)
289                 return breakStatus;
290         }
291
292         if (marker == 0) { // KDL: need to do this if thrown or if split?
293
// configure id
294
area.getIDReferences().configureID(id, area);
295         }
296
297         int spaceLeft = area.spaceLeft();
298
299         this.areaContainer =
300             new AreaContainer(propMgr.getFontState(area.getFontInfo()), 0, 0,
301                               area.getContentWidth(), spaceLeft,
302                               Position.RELATIVE);
303         areaContainer.foCreator = this; // G Seshadri
304
areaContainer.setPage(area.getPage());
305         areaContainer.setParent(area);
306
307         areaContainer.setBackground(propMgr.getBackgroundProps());
308         areaContainer.start();
309
310         areaContainer.setAbsoluteHeight(area.getAbsoluteHeight());
311         areaContainer.setIDReferences(area.getIDReferences());
312
313         largestCellHeight = minHeight;
314
315         // Flag indicaing whether any cell didn't fit in available space
316
boolean someCellDidNotLayoutCompletely = false;
317
318         /*
319          * If it takes multiple calls to completely layout the row,
320          * we need to process all of the children (cells)
321          * not just those from the marker so that the borders
322          * will be drawn properly.
323          */

324         int offset = 0; // Offset of each cell from table start edge
325
int iColIndex = 0; // 1-based column index
326
/*
327          * Ideas: set offset on each column when they are initialized
328          * no need to calculate for each row.
329          * Pass column object to cell to get offset and width and border
330          * info if borders are "collapsed".
331          */

332         for (int i = 0; i < columns.size(); i++) {
333             TableCell cell;
334             ++iColIndex;
335             TableColumn tcol = (TableColumn)columns.get(i);
336             int colWidth = tcol.getColumnWidth();
337             if (cellArray.getCellType(iColIndex) == CellArray.CELLSTART) {
338                 cell = cellArray.getCell(iColIndex);
339             } else {
340                 /*
341                  * If this cell is spanned from a previous row,
342                  * and this is the last row, get the remaining height
343                  * and use it to increase maxCellHeight if necessary
344                  */

345                 if (rowSpanMgr.isInLastRow(iColIndex)) {
346                     int h = rowSpanMgr.getRemainingHeight(iColIndex);
347                     if (h > largestCellHeight)
348                         largestCellHeight = h;
349                 }
350                 offset += colWidth;
351                 continue;
352             }
353             // cell.setTableColumn(tcol);
354
cell.setStartOffset(offset);
355             offset += colWidth;
356
357
358             int rowSpan = cell.getNumRowsSpanned();
359             int status;
360             if (Status.isIncomplete((status = cell.layout(areaContainer)))) {
361                if ((keepTogether.getType() == KeepValue.KEEP_WITH_ALWAYS && bIgnoreKeepTogether==false)
362                         || (status == Status.AREA_FULL_NONE)
363                         || rowSpan > 1) {
364                     // We will put this row into the next column/page
365
// Note: the only time this shouldn't be honored is
366
// if this row is at the top of the column area.
367
// Remove spanning cells from RowSpanMgr?
368
this.resetMarker();
369                     this.removeID(area.getIDReferences());
370                     return Status.AREA_FULL_NONE;
371                 } else if (status == Status.AREA_FULL_SOME) {
372                     /*
373                      * Row is not keep-together, cell isn't spanning
374                      * and part of it fits. We can break the cell and
375                      * the row.
376                      */

377                     someCellDidNotLayoutCompletely = true;
378                 }
379             } // else {
380
// layout was complete for a particular cell
381
int h = cell.getHeight(); // allocation height of cell
382
if (rowSpan > 1) { // pass cell fo or area???
383
rowSpanMgr.addRowSpan(cell, iColIndex,
384                                       cell.getNumColumnsSpanned(), h,
385                                       rowSpan);
386             } else if (h > largestCellHeight) {
387                 largestCellHeight = h;
388             }
389             // }
390
} // end of loop over all columns/cells
391

392         // This is in case a float was composed in the cells
393
area.setMaxHeight(area.getMaxHeight() - spaceLeft
394                           + this.areaContainer.getMaxHeight());
395
396         // Only do this for "STARTCELL", ending spans are handled separately
397
// What about empty cells? Yes, we should set their height too!
398
for (int iCol = 1; iCol <= columns.size(); iCol++) {
399             if (cellArray.getCellType(iCol) == CellArray.CELLSTART
400                     && rowSpanMgr.isSpanned(iCol) == false) {
401                 cellArray.getCell(iCol).setRowHeight(largestCellHeight);
402             }
403         }
404
405         // Adjust spanning row information
406
// ??? what if some cells are broken???
407
rowSpanMgr.finishRow(largestCellHeight);
408
409         area.addChild(areaContainer);
410         areaContainer.setHeight(largestCellHeight);
411         areaAdded = true;
412         areaContainer.end();
413
414         /*
415          * The method addDisplaySpace increases both the content
416          * height of the parent area (table body, head or footer) and
417          * also its "absolute height". So we don't need to do this
418          * explicitly.
419          *
420          * Note: it doesn't look from the CR as though we should take
421          * into account borders and padding on rows, only background.
422          * The exception is perhaps if the borders are "collapsed", but
423          * they should still be rendered only on cells and not on the
424          * rows themselves. (Karen Lease - 01may2001)
425          */

426         area.addDisplaySpace(largestCellHeight
427                              + areaContainer.getPaddingTop()
428                              + areaContainer.getBorderTopWidth()
429                              + areaContainer.getPaddingBottom()
430                              + areaContainer.getBorderBottomWidth());
431
432
433         // replaced by Hani Elabed 11/27/2000
434
// return new Status(Status.OK);
435

436         if (someCellDidNotLayoutCompletely) {
437             return Status.AREA_FULL_SOME;
438         } else {
439             if (rowSpanMgr.hasUnfinishedSpans()) {
440                 // Ignore break after if row span!
441
return Status.KEEP_WITH_NEXT;
442             }
443             if (breakAfter == BreakAfter.PAGE) {
444                 this.marker = BREAK_AFTER;
445                 return Status.FORCE_PAGE_BREAK;
446             }
447
448             if (breakAfter == BreakAfter.ODD_PAGE) {
449                 this.marker = BREAK_AFTER;
450                 return Status.FORCE_PAGE_BREAK_ODD;
451             }
452
453             if (breakAfter == BreakAfter.EVEN_PAGE) {
454                 this.marker = BREAK_AFTER;
455                 return Status.FORCE_PAGE_BREAK_EVEN;
456             }
457
458             if (breakAfter == BreakAfter.COLUMN) {
459                 this.marker = BREAK_AFTER;
460                 return Status.FORCE_COLUMN_BREAK;
461             }
462             if (keepWithNext.getType() != KeepValue.KEEP_WITH_AUTO) {
463                 return Status.KEEP_WITH_NEXT;
464             }
465             return Status.OK;
466         }
467
468     }
469
470     public int getAreaHeight() {
471         return areaContainer.getHeight();
472     }
473
474     public void removeLayout(Area area) {
475         if (areaAdded) {
476             area.removeChild(areaContainer);
477             areaAdded = false;
478         }
479         this.resetMarker();
480         this.removeID(area.getIDReferences());
481     }
482
483     public void resetMarker() {
484         super.resetMarker();
485         // Just reset all the states to not laid out and fix up row spans
486
}
487
488     /**
489      * Called by parent FO to initialize information about
490      * cells started in previous rows which span into this row.
491      * The layout operation modifies rowSpanMgr
492      */

493     public void setRowSpanMgr(RowSpanMgr rowSpanMgr) {
494         this.rowSpanMgr = rowSpanMgr;
495     }
496
497     /**
498      * Before starting layout for the first time, initialize information
499      * about spanning rows, empty cells and spanning columns.
500      */

501     private void initCellArray() {
502         cellArray = new CellArray(rowSpanMgr, columns.size());
503         int colNum = 1;
504         for (int i = 0; i< children.size(); i++) {
505             colNum = cellArray.getNextFreeCell(colNum);
506             // If off the end, the rest of the cells had better be
507
// explicitly positioned!!! (returns -1)
508

509             TableCell cell = (TableCell)children.get(i);
510             int numCols = cell.getNumColumnsSpanned();
511             int numRows = cell.getNumRowsSpanned();
512             int cellColNum = cell.getColumnNumber();
513
514             if (cellColNum == 0) {
515                 // Not explicitly specified, so put in next available colummn
516
// cell.setColumnNumber(colNum);
517
// If cellColNum "off the end", this cell is in limbo!
518
if (colNum < 1) {
519                     // ERROR!!!
520
continue;
521                 } else
522                     cellColNum = colNum;
523             } else if (cellColNum > columns.size()) {
524                 // Explicit specification out of range!
525
// Skip it and print an ERROR MESSAGE
526
continue;
527             }
528             // see if it fits and doesn't overwrite anything
529
if (cellColNum + numCols - 1 > columns.size()) {
530                 // MESSAGE: TOO MANY COLUMNS SPANNED!
531
numCols = columns.size() - cellColNum + 1;
532             }
533             // Check for overwriting other cells (returns false)
534
if (cellArray.storeCell(cell, cellColNum, numCols) == false) {
535                 // Print out some kind of warning message.
536
}
537             if (cellColNum > colNum) {
538                 // Cells are initialized as empty already
539
colNum = cellColNum;
540             } else if (cellColNum < colNum) {
541                 // MESSAGE ? cells out of order?
542
colNum = cellColNum; // CR "to the letter"!
543
}
544             int cellWidth = getCellWidth(cellColNum, numCols);
545             cell.setWidth(cellWidth);
546             colNum += numCols; // next cell in this column
547
}
548     }
549
550     // ATTENTION if startCol + numCols > number of columns in table!
551
private int getCellWidth(int startCol, int numCols) {
552         int width = 0;
553         for (int count = 0; count < numCols; count++) {
554             width += ((TableColumn)columns.get(startCol + count
555                                                      - 1)).getColumnWidth();
556         }
557         return width;
558     }
559     
560     void setIgnoreKeepTogether(boolean bIgnoreKeepTogether) {
561         this.bIgnoreKeepTogether = bIgnoreKeepTogether;
562     }
563
564 }
565
Popular Tags