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: ConditionalBorder.java 1480018 2013-05-07 18:53:29Z vhennebert $ */
19 
20 package org.apache.fop.fo.flow.table;
21 
22 import org.apache.fop.layoutmgr.table.CollapsingBorderModel;
23 
24 /**
25  * A class that holds the three possible values for a border-before/after on a table-cell,
26  * in the collapsing model. These three values are (for border-before, similar for
27  * border-after):
28  * <ul>
29  * <li>normal: common case, when a cell follows the cell before on a same page;</li>
30  * <li>leading: when the table is broken and the cell appears at the top of a page, in
31  * which case its border must be resolved with the header (or the top of the table)
32  * instead of with the previous cell;</li>
33  * <li>rest: when a cell is broken over several pages; same as leading but with
34  * conditionality taken into account.</li>
35  * </ul>
36  */
37 public class ConditionalBorder {
38 
39     /** normal border */
40     public static final int NORMAL = 0;
41 
42     /** leading and trailing border */
43     public static final int LEADING_TRAILING = 1;
44 
45     /** all the rest */
46     public static final int REST = 2;
47 
48     /** Normal case, no break. */
49     BorderSpecification normal;
50 
51     /** Special case: the cell is at the top or the bottom of the page. */
52     BorderSpecification leadingTrailing;
53 
54     /** Special case: break inside the cell. */
55     BorderSpecification rest;
56 
57     /** The model used to resolve borders. */
58     private CollapsingBorderModel collapsingBorderModel;
59 
ConditionalBorder(BorderSpecification normal, BorderSpecification leadingTrailing, BorderSpecification rest, CollapsingBorderModel collapsingBorderModel)60     private ConditionalBorder(BorderSpecification normal,
61             BorderSpecification leadingTrailing, BorderSpecification rest,
62             CollapsingBorderModel collapsingBorderModel) {
63         assert collapsingBorderModel != null;
64         this.normal = normal;
65         this.leadingTrailing = leadingTrailing;
66         this.rest = rest;
67         this.collapsingBorderModel = collapsingBorderModel;
68     }
69 
70     /**
71      * Creates a new conditional border.
72      *
73      * @param borderSpecification the border specification to take as a basis
74      * @param collapsingBorderModel the model that will be used to resolved borders
75      */
ConditionalBorder(BorderSpecification borderSpecification, CollapsingBorderModel collapsingBorderModel)76     ConditionalBorder(BorderSpecification borderSpecification,
77             CollapsingBorderModel collapsingBorderModel) {
78         this (borderSpecification, borderSpecification,
79                borderSpecification.getBorderInfo().getWidth().isDiscard()
80                  ? BorderSpecification.getDefaultBorder() : borderSpecification,
81                collapsingBorderModel);
82     }
83 
84     /**
85      * Resolves and updates the relevant parts of this border as well as the given one.
86      *
87      * @param competitor
88      * @param withNormal
89      * @param withLeadingTrailing
90      * @param withRest
91      */
resolve(ConditionalBorder competitor, boolean withNormal, boolean withLeadingTrailing, boolean withRest)92     void resolve(ConditionalBorder competitor, boolean withNormal,
93             boolean withLeadingTrailing, boolean withRest) {
94         if (withNormal) {
95             BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
96                     normal, competitor.normal);
97             if (resolvedBorder != null) {
98                 normal = resolvedBorder;
99                 competitor.normal = resolvedBorder;
100             }
101         }
102         if (withLeadingTrailing) {
103             BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
104                     leadingTrailing, competitor.leadingTrailing);
105             if (resolvedBorder != null) {
106                 leadingTrailing = resolvedBorder;
107                 competitor.leadingTrailing = resolvedBorder;
108             }
109         }
110         if (withRest) {
111             BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(rest,
112                     competitor.rest);
113             if (resolvedBorder != null) {
114                 rest = resolvedBorder;
115                 competitor.rest = resolvedBorder;
116             }
117         }
118     }
119 
120     /**
121      * Integrates the given segment in this border. Unlike for
122      * {@link #integrateSegment(ConditionalBorder, boolean, boolean, boolean)}, this
123      * method nicely handles the case where the CollapsingBorderModel returns null, by
124      * keeping the components to their old values.
125      *
126      * @param competitor
127      * @param withNormal
128      * @param withLeadingTrailing
129      * @param withRest
130      */
integrateCompetingSegment(ConditionalBorder competitor, boolean withNormal, boolean withLeadingTrailing, boolean withRest)131     void integrateCompetingSegment(ConditionalBorder competitor, boolean withNormal,
132             boolean withLeadingTrailing, boolean withRest) {
133         if (withNormal) {
134             BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
135                     normal, competitor.normal);
136             if (resolvedBorder != null) {
137                 normal = resolvedBorder;
138             }
139         }
140         if (withLeadingTrailing) {
141             BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
142                     leadingTrailing, competitor.leadingTrailing);
143             if (resolvedBorder != null) {
144                 leadingTrailing = resolvedBorder;
145             }
146         }
147         if (withRest) {
148             BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(rest,
149                     competitor.rest);
150             if (resolvedBorder != null) {
151                 rest = resolvedBorder;
152             }
153         }
154     }
155 
156     /**
157      * Updates this border after taking into account the given segment. The
158      * CollapsingBorderModel is not expected to return null.
159      *
160      * @param segment
161      * @param withNormal
162      * @param withLeadingTrailing
163      * @param withRest
164      */
integrateSegment(ConditionalBorder segment, boolean withNormal, boolean withLeadingTrailing, boolean withRest)165     void integrateSegment(ConditionalBorder segment, boolean withNormal,
166             boolean withLeadingTrailing, boolean withRest) {
167         if (withNormal) {
168             normal = collapsingBorderModel.determineWinner(normal, segment.normal);
169             assert normal != null;
170         }
171         if (withLeadingTrailing) {
172             leadingTrailing = collapsingBorderModel.determineWinner(leadingTrailing,
173                     segment.leadingTrailing);
174             assert leadingTrailing != null;
175         }
176         if (withRest) {
177             rest = collapsingBorderModel.determineWinner(rest, segment.rest);
178             assert rest != null;
179         }
180     }
181 
182     /**
183      * Returns a shallow copy of this border.
184      *
185      * @return a copy of this border
186      */
copy()187     ConditionalBorder copy() {
188         return new ConditionalBorder(normal, leadingTrailing, rest, collapsingBorderModel);
189     }
190 
191     /** {@inheritDoc} */
toString()192     public String toString() {
193         return "{normal: " + normal + ", leading: " + leadingTrailing + ", rest: " + rest + "}";
194     }
195 
196     /**
197      * Returns a default border specification.
198      *
199      * @param collapsingBorderModel the model that will be used to resolve borders
200      * @return a border with style 'none' for all of the three components
201      */
getDefaultBorder(CollapsingBorderModel collapsingBorderModel)202     static ConditionalBorder getDefaultBorder(CollapsingBorderModel collapsingBorderModel) {
203         BorderSpecification defaultBorderSpec = BorderSpecification.getDefaultBorder();
204         return new ConditionalBorder(defaultBorderSpec, defaultBorderSpec, defaultBorderSpec,
205                 collapsingBorderModel);
206     }
207 }
208