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: FixedColRowGroupBuilder.java 1762060 2016-09-23 12:57:46Z ssteiner $ */
19 
20 package org.apache.fop.fo.flow.table;
21 
22 import java.util.ArrayList;
23 import java.util.List;
24 
25 import org.apache.fop.fo.Constants;
26 import org.apache.fop.fo.ValidationException;
27 
28 
29 /**
30  * A row group builder optimised for a fixed number of columns, known before the parsing
31  * of cells starts (that is, if the fo:table has explicit fo:table-column children).
32  */
33 class FixedColRowGroupBuilder extends RowGroupBuilder {
34 
35     /** Number of columns in the corresponding table. */
36     private int numberOfColumns;
37 
38     private TableRow currentTableRow;
39 
40     /** 0-based, index in the row group. */
41     private int currentRowIndex;
42 
43     /** The rows belonging to this row group. List of List of {@link GridUnit}s. */
44     private List<List<GridUnit>> rows;
45 
46     private boolean firstInPart = true;
47 
48     /** The last encountered row. This is the last row of the table if it has no footer. */
49     private List lastRow;
50 
51     private BorderResolver borderResolver;
52 
FixedColRowGroupBuilder(Table t)53     FixedColRowGroupBuilder(Table t) {
54         super(t);
55         numberOfColumns = t.getNumberOfColumns();
56         if (t.isSeparateBorderModel()) {
57             borderResolver = new SeparateBorderResolver();
58         } else {
59             borderResolver = new CollapsingBorderResolver(t);
60         }
61         initialize();
62     }
63 
64     /**
65      * Prepares this builder for creating a new row group.
66      */
initialize()67     private void initialize() {
68         rows = new ArrayList();
69         currentRowIndex = 0;
70     }
71 
72     /** {@inheritDoc} */
addTableCell(TableCell cell)73     void addTableCell(TableCell cell) {
74         for (int i = rows.size(); i < currentRowIndex + cell.getNumberRowsSpanned(); i++) {
75             List effRow = new ArrayList(numberOfColumns);
76             for (int j = 0; j < numberOfColumns; j++) {
77                 effRow.add(null);
78             }
79             rows.add(effRow);
80         }
81         int columnIndex = cell.getColumnNumber() - 1;
82         PrimaryGridUnit pgu = new PrimaryGridUnit(cell, columnIndex);
83         List row = (List) rows.get(currentRowIndex);
84         row.set(columnIndex, pgu);
85         // TODO
86         GridUnit[] cellRow = new GridUnit[cell.getNumberColumnsSpanned()];
87         cellRow[0] = pgu;
88         for (int j = 1; j < cell.getNumberColumnsSpanned(); j++) {
89             GridUnit gu = new GridUnit(pgu, j, 0);
90             row.set(columnIndex + j, gu);
91             cellRow[j] = gu;
92         }
93         pgu.addRow(cellRow);
94         for (int i = 1; i < cell.getNumberRowsSpanned(); i++) {
95             row = (List) rows.get(currentRowIndex + i);
96             cellRow = new GridUnit[cell.getNumberColumnsSpanned()];
97             for (int j = 0; j < cell.getNumberColumnsSpanned(); j++) {
98                 GridUnit gu = new GridUnit(pgu, j, i);
99                 row.set(columnIndex + j, gu);
100                 cellRow[j] = gu;
101             }
102             pgu.addRow(cellRow);
103         }
104     }
105 
setFlagForCols(int flag, List row)106     private static void setFlagForCols(int flag, List row) {
107         for (Object aRow : row) {
108             ((GridUnit) aRow).setFlag(flag);
109         }
110     }
111 
112     /** {@inheritDoc} */
startTableRow(TableRow tableRow)113     void startTableRow(TableRow tableRow) {
114         currentTableRow = tableRow;
115     }
116 
117     /** {@inheritDoc} */
endTableRow()118     void endTableRow() {
119         assert currentTableRow != null;
120         if (currentRowIndex > 0 && currentTableRow.getBreakBefore() != Constants.EN_AUTO) {
121             TableEventProducer eventProducer = TableEventProducer.Provider.get(
122                     currentTableRow.getUserAgent().getEventBroadcaster());
123             eventProducer.breakIgnoredDueToRowSpanning(this, currentTableRow.getName(), true,
124                     currentTableRow.getLocator());
125         }
126         if (currentRowIndex < rows.size() - 1
127                 && currentTableRow.getBreakAfter() != Constants.EN_AUTO) {
128             TableEventProducer eventProducer = TableEventProducer.Provider.get(
129                     currentTableRow.getUserAgent().getEventBroadcaster());
130             eventProducer.breakIgnoredDueToRowSpanning(this, currentTableRow.getName(), false,
131                     currentTableRow.getLocator());
132         }
133         for (Object o : ((List) rows.get(currentRowIndex))) {
134             GridUnit gu = (GridUnit) o;
135             // The row hasn't been filled with empty grid units yet
136             if (gu != null) {
137                 gu.setRow(currentTableRow);
138             }
139         }
140         handleRowEnd(currentTableRow);
141     }
142 
143     /** {@inheritDoc} */
endRow(TablePart part)144     void endRow(TablePart part) {
145         handleRowEnd(part);
146     }
147 
handleRowEnd(TableCellContainer container)148     private void handleRowEnd(TableCellContainer container) {
149         List currentRow = (List) rows.get(currentRowIndex);
150         lastRow = currentRow;
151         // Fill gaps with empty grid units
152         for (int i = 0; i < numberOfColumns; i++) {
153             if (currentRow.get(i) == null) {
154                 currentRow.set(i, new EmptyGridUnit(table, currentTableRow, i));
155             }
156         }
157         borderResolver.endRow(currentRow, container);
158         if (firstInPart) {
159             setFlagForCols(GridUnit.FIRST_IN_PART, currentRow);
160             firstInPart = false;
161         }
162         if (currentRowIndex == rows.size() - 1) {
163             // Means that the current row has no cell spanning over following rows
164             container.getTablePart().addRowGroup(rows);
165             initialize();
166         } else {
167             currentRowIndex++;
168         }
169         currentTableRow = null;
170     }
171 
172     /** {@inheritDoc} */
startTablePart(TablePart part)173     void startTablePart(TablePart part) {
174         firstInPart = true;
175         borderResolver.startPart(part);
176     }
177 
178     /** {@inheritDoc} */
endTablePart()179     void endTablePart() throws ValidationException {
180         if (rows.size() > 0) {
181             throw new ValidationException(
182                     "A table-cell is spanning more rows than available in its parent element.");
183         }
184         setFlagForCols(GridUnit.LAST_IN_PART, lastRow);
185         borderResolver.endPart();
186     }
187 
188     /** {@inheritDoc} */
endTable()189     void endTable() {
190         borderResolver.endTable();
191     }
192 }
193