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