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: GridUnit.java 1610839 2014-07-15 20:25:58Z vhennebert $ */
19 
20 package org.apache.fop.fo.flow.table;
21 
22 import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
23 import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
24 import org.apache.fop.layoutmgr.table.CollapsingBorderModel;
25 
26 /**
27  * This class represents one grid unit inside a table.
28  */
29 public class GridUnit {
30 
31     /**
32      * Indicates that the grid unit is in the first row of the table part (header, footer,
33      * body).
34      */
35     public static final int FIRST_IN_PART = 0;
36 
37     /**
38      * Indicates that the grid unit is in the last row of the table part (header, footer,
39      * body).
40      */
41     public static final int LAST_IN_PART = 1;
42 
43     /** Indicates that the primary grid unit has a pending keep-with-next. */
44     public static final int KEEP_WITH_NEXT_PENDING = 2;
45 
46     /** Indicates that the primary grid unit has a pending keep-with-previous. */
47     public static final int KEEP_WITH_PREVIOUS_PENDING = 3;
48 
49     /** Primary grid unit */
50     private PrimaryGridUnit primary;
51 
52     /** Table cell which occupies this grid unit */
53     protected TableCell cell;
54 
55     /** Table row occupied by this grid unit (may be null). */
56     private TableRow row;
57 
58     /** index of grid unit within cell in column direction */
59     private int colSpanIndex;
60 
61     /** index of grid unit within cell in row direction */
62     private int rowSpanIndex;
63 
64     /** flags for the grid unit */
65     private byte flags;
66 
67     /** the border-before specification */
68     ConditionalBorder borderBefore;
69     /** the border-after specification */
70     ConditionalBorder borderAfter;
71     /** the border-start specification */
72     BorderSpecification borderStart;
73     /** the border-end specification */
74     BorderSpecification borderEnd;
75 
76     /** The border model helper associated with the table */
77     protected CollapsingBorderModel collapsingBorderModel;
78 
79     /**
80      * Creates a new grid unit.
81      *
82      * @param table the containing table
83      * @param colSpanIndex index of this grid unit in the span, in column direction
84      * @param rowSpanIndex index of this grid unit in the span, in row direction
85      */
GridUnit(Table table, int colSpanIndex, int rowSpanIndex)86     protected GridUnit(Table table, int colSpanIndex, int rowSpanIndex) {
87         this(colSpanIndex, rowSpanIndex);
88         setBorders(table);
89     }
90 
91     /**
92      * Creates a new grid unit.
93      *
94      * @param cell table cell which occupies this grid unit
95      * @param colSpanIndex index of this grid unit in the span, in column direction
96      * @param rowSpanIndex index of this grid unit in the span, in row direction
97      */
GridUnit(TableCell cell, int colSpanIndex, int rowSpanIndex)98     protected GridUnit(TableCell cell, int colSpanIndex, int rowSpanIndex) {
99         this(colSpanIndex, rowSpanIndex);
100         this.cell = cell;
101         setBorders(cell.getTable());
102     }
103 
104     /**
105      * Creates a new grid unit.
106      *
107      * @param primary the before-start grid unit of the cell containing this grid unit
108      * @param colSpanIndex index of this grid unit in the span, in column direction
109      * @param rowSpanIndex index of this grid unit in the span, in row direction
110      */
GridUnit(PrimaryGridUnit primary, int colSpanIndex, int rowSpanIndex)111     GridUnit(PrimaryGridUnit primary, int colSpanIndex, int rowSpanIndex) {
112         this(primary.getCell(), colSpanIndex, rowSpanIndex);
113         this.primary = primary;
114     }
115 
GridUnit(int colSpanIndex, int rowSpanIndex)116     private GridUnit(int colSpanIndex, int rowSpanIndex) {
117         this.colSpanIndex = colSpanIndex;
118         this.rowSpanIndex = rowSpanIndex;
119     }
120 
setBorders(Table table )121     private void setBorders(Table table/*TODO*/) {
122         if (!table.isSeparateBorderModel()) {
123             collapsingBorderModel = CollapsingBorderModel.getBorderModelFor(table
124                     .getBorderCollapse());
125             setBordersFromCell();
126         }
127     }
128 
129     /**
130      * Prepares the borders of this grid unit for upcoming resolution, in the collapsing
131      * model.
132      */
setBordersFromCell()133     protected void setBordersFromCell() {
134         borderBefore = cell.borderBefore.copy();
135         if (rowSpanIndex > 0) {
136             borderBefore.normal = BorderSpecification.getDefaultBorder();
137         }
138         borderAfter = cell.borderAfter.copy();
139         if (!isLastGridUnitRowSpan()) {
140             borderAfter.normal = BorderSpecification.getDefaultBorder();
141         }
142         if (colSpanIndex == 0) {
143             borderStart = cell.borderStart;
144         } else {
145             borderStart = BorderSpecification.getDefaultBorder();
146         }
147         if (isLastGridUnitColSpan()) {
148             borderEnd = cell.borderEnd;
149         } else {
150             borderEnd = BorderSpecification.getDefaultBorder();
151         }
152     }
153 
154     /**
155      * Returns the table cell associated with this grid unit.
156      * @return the table cell
157      */
getCell()158     public TableCell getCell() {
159         return cell;
160     }
161 
162     /**
163      * Returns the fo:table-row element (if any) this grid unit belongs to.
164      *
165      * @return the row containing this grid unit, or null if there is no fo:table-row
166      * element in the corresponding table-part
167      */
getRow()168     public TableRow getRow() {
169         return row;
170     }
171 
setRow(TableRow row)172     void setRow(TableRow row) {
173         this.row = row;
174     }
175 
176     /**
177      * Returns the before-start grid unit of the cell containing this grid unit.
178      *
179      * @return the before-start grid unit of the cell containing this grid unit.
180      */
getPrimary()181     public PrimaryGridUnit getPrimary() {
182         return primary;
183     }
184 
185     /**
186      * Is this grid unit the before-start grid unit of the cell?
187      *
188      * @return true if this grid unit is the before-start grid unit of the cell
189      */
isPrimary()190     public boolean isPrimary() {
191         return false;
192     }
193 
194     /**
195      * Does this grid unit belong to an empty cell?
196      *
197      * @return true if this grid unit belongs to an empty cell
198      */
isEmpty()199     public boolean isEmpty() {
200         return cell == null;
201     }
202 
203     /** @return true if the grid unit is the last in column spanning direction */
isLastGridUnitColSpan()204     public boolean isLastGridUnitColSpan() {
205         return (colSpanIndex == cell.getNumberColumnsSpanned() - 1);
206     }
207 
208     /** @return true if the grid unit is the last in row spanning direction */
isLastGridUnitRowSpan()209     public boolean isLastGridUnitRowSpan() {
210         return (rowSpanIndex == cell.getNumberRowsSpanned() - 1);
211     }
212 
213     /**
214      * @return the index of the grid unit inside a cell in row direction
215      */
getRowSpanIndex()216     public int getRowSpanIndex() {
217         return rowSpanIndex;
218     }
219 
220     /**
221      * @return the index of the grid unit inside a cell in column direction
222      */
getColSpanIndex()223     public int getColSpanIndex() {
224         return colSpanIndex;
225     }
226 
227     /**
228      * Returns the resolved border-before of this grid unit, in the collapsing-border
229      * model.
230      *
231      * @param which one of {@link ConditionalBorder#NORMAL},
232      * {@link ConditionalBorder#LEADING_TRAILING} or {@link ConditionalBorder#REST}
233      * @return the corresponding border
234      */
getBorderBefore(int which)235     public BorderInfo getBorderBefore(int which) {
236         switch (which) {
237         case ConditionalBorder.NORMAL:
238             return borderBefore.normal.getBorderInfo();
239         case ConditionalBorder.LEADING_TRAILING:
240             return borderBefore.leadingTrailing.getBorderInfo();
241         case ConditionalBorder.REST:
242             return borderBefore.rest.getBorderInfo();
243         default:
244             assert false;
245             return null;
246         }
247     }
248 
249     /**
250      * Returns the resolved border-after of this grid unit, in the collapsing-border
251      * model.
252      *
253      * @param which one of {@link ConditionalBorder#NORMAL},
254      * {@link ConditionalBorder#LEADING_TRAILING} or {@link ConditionalBorder#REST}
255      * @return the corresponding border
256      */
getBorderAfter(int which)257     public BorderInfo getBorderAfter(int which) {
258         switch (which) {
259         case ConditionalBorder.NORMAL:
260             return borderAfter.normal.getBorderInfo();
261         case ConditionalBorder.LEADING_TRAILING:
262             return borderAfter.leadingTrailing.getBorderInfo();
263         case ConditionalBorder.REST:
264             return borderAfter.rest.getBorderInfo();
265         default:
266             assert false;
267             return null;
268         }
269     }
270 
271     /**
272      * Returns the resolved border-start of this grid unit, in the collapsing-border
273      * model.
274      *
275      * @return the corresponding border
276      */
getBorderStart()277     public BorderInfo getBorderStart() {
278         return borderStart.getBorderInfo();
279     }
280 
281     /**
282      * Returns the resolved border-end of this grid unit, in the collapsing-border
283      * model.
284      *
285      * @return the corresponding border
286      */
getBorderEnd()287     public BorderInfo getBorderEnd() {
288         return borderEnd.getBorderInfo();
289     }
290 
291     /**
292      * Resolve collapsing borders for the given cell. Used in case of the collapsing
293      * border model.
294      *
295      * @param other neighbouring grid unit
296      * @param side the side to resolve (one of
297      * CommonBorderPaddingBackground.BEFORE|AFTER|START|END)
298      */
resolveBorder(GridUnit other, int side)299     void resolveBorder(GridUnit other, int side) {
300         switch (side) {
301         case CommonBorderPaddingBackground.BEFORE:
302             borderBefore.resolve(other.borderAfter, true, false, false);
303             break;
304         case CommonBorderPaddingBackground.AFTER:
305             borderAfter.resolve(other.borderBefore, true, false, false);
306             break;
307         case CommonBorderPaddingBackground.START:
308             BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
309                     borderStart, other.borderEnd);
310             if (resolvedBorder != null) {
311                 this.borderStart = resolvedBorder;
312                 other.borderEnd = resolvedBorder;
313             }
314             break;
315         case CommonBorderPaddingBackground.END:
316             resolvedBorder = collapsingBorderModel.determineWinner(
317                     borderEnd, other.borderStart);
318             if (resolvedBorder != null) {
319                 this.borderEnd = resolvedBorder;
320                 other.borderStart = resolvedBorder;
321             }
322             break;
323         default: assert false;
324         }
325     }
326 
327     /**
328      * For the given side, integrates in the conflict resolution the border segment of the
329      * given parent element.
330      *
331      * @param side the side to consider (either CommonBorderPaddingBackground.BEFORE or
332      * AFTER)
333      * @param parent a table element whose corresponding border coincides on the given
334      * side
335      */
integrateBorderSegment(int side, TableFObj parent, boolean withNormal, boolean withLeadingTrailing, boolean withRest)336     void integrateBorderSegment(int side, TableFObj parent, boolean withNormal,
337             boolean withLeadingTrailing, boolean withRest) {
338         switch (side) {
339         case CommonBorderPaddingBackground.BEFORE:
340             borderBefore.integrateSegment(parent.borderBefore, withNormal,
341                     withLeadingTrailing, withRest);
342             break;
343         case CommonBorderPaddingBackground.AFTER:
344             borderAfter.integrateSegment(parent.borderAfter, withNormal,
345                     withLeadingTrailing, withRest);
346             break;
347         default: assert false;
348         }
349     }
350 
351     /**
352      * For the given side, integrates in the conflict resolution the border segment of the
353      * given parent element.
354      *
355      * @param side the side to consider (one of
356      * CommonBorderPaddingBackground.BEFORE|AFTER|START|END)
357      * @param parent a table element whose corresponding border coincides on the given side
358      */
integrateBorderSegment(int side, TableFObj parent)359     void integrateBorderSegment(int side, TableFObj parent) {
360         switch (side) {
361         case CommonBorderPaddingBackground.BEFORE:
362         case CommonBorderPaddingBackground.AFTER:
363             integrateBorderSegment(side, parent, true, true, true);
364             break;
365         case CommonBorderPaddingBackground.START:
366             borderStart = collapsingBorderModel.determineWinner(borderStart,
367                     parent.borderStart);
368             break;
369         case CommonBorderPaddingBackground.END:
370             borderEnd = collapsingBorderModel.determineWinner(borderEnd,
371                     parent.borderEnd);
372             break;
373         default: assert false;
374         }
375     }
376 
377     /**
378      * For the given side, integrates in the conflict resolution the given border segment.
379      *
380      * @param side the side to consider (one of CommonBorderPaddingBackground.START|END)
381      * @param segment a border specification to integrate at the given side
382      */
integrateBorderSegment(int side, BorderSpecification segment)383     void integrateBorderSegment(int side, BorderSpecification segment) {
384         switch(side) {
385         case CommonBorderPaddingBackground.START:
386             borderStart = collapsingBorderModel.determineWinner(borderStart, segment);
387             break;
388         case CommonBorderPaddingBackground.END:
389             borderEnd = collapsingBorderModel.determineWinner(borderEnd, segment);
390             break;
391         default: assert false;
392         }
393     }
394 
integrateCompetingBorder(int side, ConditionalBorder competitor, boolean withNormal, boolean withLeadingTrailing, boolean withRest)395     void integrateCompetingBorder(int side, ConditionalBorder competitor,
396             boolean withNormal, boolean withLeadingTrailing, boolean withRest) {
397         switch (side) {
398         case CommonBorderPaddingBackground.BEFORE:
399             borderBefore.integrateCompetingSegment(competitor, withNormal,
400                     withLeadingTrailing, withRest);
401             break;
402         case CommonBorderPaddingBackground.AFTER:
403             borderAfter.integrateCompetingSegment(competitor, withNormal,
404                     withLeadingTrailing, withRest);
405             break;
406         default: assert false;
407         }
408     }
409 
410     /**
411      * Returns a flag for this GridUnit.
412      *
413      * @param which the requested flag
414      * @return the value of the flag
415      */
getFlag(int which)416     public boolean getFlag(int which) {
417         return (flags & (1 << which)) != 0;
418     }
419 
420     /**
421      * Sets a flag on a GridUnit.
422      *
423      * @param which the flag to set
424      * @param value the new value for the flag
425      */
setFlag(int which, boolean value)426     public void setFlag(int which, boolean value) {
427         if (value) {
428             flags |= (1 << which); // set flag
429         } else {
430             flags &= ~(1 << which); // clear flag
431         }
432     }
433 
434     /**
435      * Sets the given flag on this grid unit.
436      *
437      * @param which the flag to set
438      */
setFlag(int which)439     public void setFlag(int which) {
440         setFlag(which, true);
441     }
442 
443     /** {@inheritDoc} */
toString()444     public String toString() {
445         StringBuffer buffer = new StringBuffer();
446         if (isEmpty()) {
447             buffer.append("EMPTY");
448         } else if (isPrimary()) {
449             buffer.append("Primary");
450         }
451         buffer.append("GridUnit:");
452         if (colSpanIndex > 0) {
453             buffer.append(" colSpan=").append(colSpanIndex);
454             if (isLastGridUnitColSpan()) {
455                 buffer.append("(last)");
456             }
457         }
458         if (rowSpanIndex > 0) {
459             buffer.append(" rowSpan=").append(rowSpanIndex);
460             if (isLastGridUnitRowSpan()) {
461                 buffer.append("(last)");
462             }
463         }
464         if (!isPrimary() && getPrimary() != null) {
465             buffer.append(" primary=").append(getPrimary().getRowIndex());
466             buffer.append("/").append(getPrimary().getColIndex());
467             if (getPrimary().getCell() != null) {
468                 buffer.append(" id=" + getPrimary().getCell().getId());
469             }
470         }
471         buffer.append(" flags=").append(Integer.toBinaryString(flags));
472         return buffer.toString();
473     }
474 
475 }
476