Skip to content

Commit 270a7d7

Browse files
committed
Correctly handle border style and colors for merged regions
1 parent 82f43ab commit 270a7d7

13 files changed

+422
-149
lines changed

src/main/groovy/com/jameskleeh/excel/CellStyleBuilder.groovy

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,18 @@ import com.jameskleeh.excel.style.CellStyleBorderStyleApplier
2424
import groovy.transform.CompileStatic
2525
import groovy.transform.TypeCheckingMode
2626
import org.apache.poi.ss.usermodel.BorderStyle
27-
import org.apache.poi.ss.usermodel.CellStyle
2827
import org.apache.poi.ss.usermodel.FillPatternType
2928
import org.apache.poi.ss.usermodel.Font as FontType
3029
import org.apache.poi.ss.usermodel.HorizontalAlignment
3130
import org.apache.poi.ss.usermodel.VerticalAlignment
32-
import org.apache.poi.ss.util.CellRangeAddress
33-
import org.apache.poi.ss.util.RegionUtil
3431
import org.apache.poi.xssf.usermodel.XSSFCell
3532
import org.apache.poi.xssf.usermodel.XSSFCellStyle
3633
import org.apache.poi.xssf.usermodel.XSSFColor
3734
import org.apache.poi.xssf.usermodel.XSSFFont
3835
import org.apache.poi.xssf.usermodel.XSSFRow
39-
import org.apache.poi.xssf.usermodel.XSSFSheet
4036
import org.apache.poi.xssf.usermodel.XSSFWorkbook
4137
import org.apache.poi.xssf.usermodel.extensions.XSSFCellBorder.BorderSide
42-
4338
import java.awt.Color
44-
import java.lang.reflect.Method
4539

4640
/**
4741
* A class to build an {@link org.apache.poi.xssf.usermodel.XSSFCellStyle} from a map
@@ -234,18 +228,18 @@ class CellStyleBuilder {
234228
}
235229

236230
private void setBorder(Map border, BorderSide side, BorderStyleApplier styleApplier) {
237-
final String key = side.name()
238-
if (border.containsKey(key)) {
239-
if (border[key] instanceof Map) {
240-
Map edge = (Map) border[key]
231+
final String KEY = side.name().toLowerCase()
232+
if (border.containsKey(KEY)) {
233+
if (border[KEY] instanceof Map) {
234+
Map edge = (Map) border[KEY]
241235
if (edge.containsKey(COLOR)) {
242236
styleApplier.applyColor(side, getColor(edge[COLOR]))
243237
}
244238
if (edge.containsKey(STYLE)) {
245239
styleApplier.applyStyle(side, getBorderStyle(edge[STYLE]))
246240
}
247241
} else {
248-
styleApplier.applyStyle(side, getBorderStyle(border[key]))
242+
styleApplier.applyStyle(side, getBorderStyle(border[KEY]))
249243
}
250244
}
251245
}
@@ -478,9 +472,9 @@ class CellStyleBuilder {
478472
}
479473
}
480474

481-
void applyBorderToRegion(CellRangeAddress range, XSSFSheet sheet, Map border) {
482-
BorderStyleApplier borderStyleApplier = new CellRangeBorderStyleApplier(range, sheet)
475+
void applyBorderToRegion(CellRangeBorderStyleApplier borderStyleApplier, Map border) {
483476
setBorder(borderStyleApplier, border)
477+
borderStyleApplier.setStyles()
484478
}
485479

486480
}

src/main/groovy/com/jameskleeh/excel/Column.groovy

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ under the License.
1919
package com.jameskleeh.excel
2020

2121
import com.jameskleeh.excel.internal.CreatesCells
22+
import com.jameskleeh.excel.style.CellRangeBorderStyleApplier
23+
import com.jameskleeh.excel.style.ColumnCellRangeBorderStyleApplier
2224
import groovy.transform.CompileStatic
2325
import org.apache.poi.ss.util.CellRangeAddress
2426
import org.apache.poi.xssf.usermodel.XSSFCell
@@ -67,26 +69,22 @@ class Column extends CreatesCells {
6769
*/
6870
@Override
6971
void merge(Map style, @DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Column) Closure callable) {
70-
Map existingDefaultOptions = defaultOptions
72+
performMerge(style, callable)
73+
}
7174

72-
if (style != null && !style.isEmpty()) {
73-
Map newDefaultOptions = new LinkedHashMap(style)
74-
styleBuilder.convertSimpleOptions(newDefaultOptions)
75-
newDefaultOptions = styleBuilder.merge(defaultOptions, newDefaultOptions)
76-
defaultOptions = newDefaultOptions
77-
}
75+
@Override
76+
protected CellRangeAddress getRange(int start, int end) {
77+
new CellRangeAddress(start, end, columnIdx, columnIdx)
78+
}
7879

79-
callable.resolveStrategy = Closure.DELEGATE_FIRST
80-
callable.delegate = this
81-
int startingRowIndex = rowIdx
82-
callable.call()
83-
int endingRowIndex = rowIdx - 1
84-
if (endingRowIndex > startingRowIndex) {
85-
CellRangeAddress range = new CellRangeAddress(startingRowIndex, endingRowIndex, columnIdx, columnIdx)
86-
sheet.addMergedRegion(range)
87-
}
80+
@Override
81+
protected int getMergeIndex() {
82+
rowIdx
83+
}
8884

89-
defaultOptions = existingDefaultOptions
85+
@Override
86+
protected CellRangeBorderStyleApplier getBorderStyleApplier(CellRangeAddress range, XSSFSheet sheet) {
87+
new ColumnCellRangeBorderStyleApplier(range, sheet)
9088
}
9189

9290
/**

src/main/groovy/com/jameskleeh/excel/Row.groovy

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ under the License.
1919
package com.jameskleeh.excel
2020

2121
import com.jameskleeh.excel.internal.CreatesCells
22+
import com.jameskleeh.excel.style.CellRangeBorderStyleApplier
23+
import com.jameskleeh.excel.style.RowCellRangeBorderStyleApplier
2224
import groovy.transform.CompileStatic
2325
import org.apache.poi.ss.util.CellRangeAddress
2426
import org.apache.poi.xssf.usermodel.XSSFCell
2527
import org.apache.poi.xssf.usermodel.XSSFRow
28+
import org.apache.poi.xssf.usermodel.XSSFSheet
2629

2730
/**
2831
* A class used to create a row in an excel document
@@ -76,29 +79,22 @@ class Row extends CreatesCells {
7679
*/
7780
@Override
7881
void merge(final Map style, @DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Row) Closure callable) {
79-
Map existingDefaultOptions = defaultOptions
82+
performMerge(style, callable)
83+
}
8084

81-
if (style != null && !style.isEmpty()) {
82-
Map newDefaultOptions = new LinkedHashMap(style)
83-
styleBuilder.convertSimpleOptions(newDefaultOptions)
84-
newDefaultOptions = styleBuilder.merge(defaultOptions, newDefaultOptions)
85-
defaultOptions = newDefaultOptions
86-
}
85+
@Override
86+
protected CellRangeAddress getRange(int start, int end) {
87+
new CellRangeAddress(row.rowNum, row.rowNum, start, end)
88+
}
8789

88-
Map borderOptions = defaultOptions.containsKey('border') ? (Map)defaultOptions.remove('border') : Collections.emptyMap()
89-
90-
callable.resolveStrategy = Closure.DELEGATE_FIRST
91-
callable.delegate = this
92-
int startingCellIndex = cellIdx
93-
callable.call()
94-
int endingCellIndex = cellIdx - 1
95-
if (endingCellIndex > startingCellIndex) {
96-
CellRangeAddress range = new CellRangeAddress(row.rowNum, row.rowNum, startingCellIndex, endingCellIndex)
97-
sheet.addMergedRegion(range)
98-
styleBuilder.applyBorderToRegion(range, sheet, borderOptions)
99-
}
90+
@Override
91+
protected int getMergeIndex() {
92+
cellIdx
93+
}
10094

101-
defaultOptions = existingDefaultOptions
95+
@Override
96+
protected CellRangeBorderStyleApplier getBorderStyleApplier(CellRangeAddress range, XSSFSheet sheet) {
97+
new RowCellRangeBorderStyleApplier(range, sheet)
10298
}
10399

104100
/**

src/main/groovy/com/jameskleeh/excel/Sheet.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,9 @@ class Sheet {
163163
if (options?.containsKey(HEIGHT)) {
164164
Object height = options[HEIGHT]
165165
if (height instanceof Short) {
166-
row.setHeight(height)
166+
row.setHeight((Short)height)
167167
} else if (height instanceof Float) {
168-
row.setHeightInPoints(height)
168+
row.setHeightInPoints((Float)height)
169169
} else {
170170
throw new IllegalArgumentException('Row height must be a short or float')
171171
}

src/main/groovy/com/jameskleeh/excel/internal/CreatesCells.groovy

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ import com.jameskleeh.excel.CellStyleBuilder
2222
import com.jameskleeh.excel.Excel
2323
import com.jameskleeh.excel.Font
2424
import com.jameskleeh.excel.CellFinder
25+
import com.jameskleeh.excel.style.CellRangeBorderStyleApplier
26+
import groovy.transform.CompileStatic
2527
import org.apache.poi.common.usermodel.Hyperlink
2628
import org.apache.poi.common.usermodel.HyperlinkType
29+
import org.apache.poi.ss.util.CellRangeAddress
2730
import org.apache.poi.xssf.usermodel.XSSFCell
2831
import org.apache.poi.xssf.usermodel.XSSFHyperlink
2932
import org.apache.poi.xssf.usermodel.XSSFSheet
@@ -36,6 +39,7 @@ import java.awt.Color
3639
*
3740
* @author James Kleeh
3841
*/
42+
@CompileStatic
3943
abstract class CreatesCells {
4044

4145
protected final XSSFWorkbook workbook
@@ -163,7 +167,7 @@ abstract class CreatesCells {
163167
* @return The native cell
164168
*/
165169
XSSFCell cell() {
166-
cell('')
170+
cell(null)
167171
}
168172

169173
/**
@@ -184,25 +188,24 @@ abstract class CreatesCells {
184188
* @return The native cell
185189
*/
186190
XSSFCell cell(Object value, final Map style) {
187-
188191
XSSFCell cell = nextCell()
189192
setStyle(value, cell, style)
190193
if (value == null) {
191-
return
194+
return cell
192195
}
193196
Closure callable = Excel.getRenderer(value.class)
194197
if (callable != null) {
195198
cell.setCellValue((String)callable.call(value))
196199
} else if (value instanceof String) {
197-
cell.setCellValue(value)
200+
cell.setCellValue((String)value)
198201
} else if (value instanceof Calendar) {
199-
cell.setCellValue(value)
202+
cell.setCellValue((Calendar)value)
200203
} else if (value instanceof Date) {
201-
cell.setCellValue(value)
204+
cell.setCellValue((Date)value)
202205
} else if (value instanceof Number) {
203-
cell.setCellValue(value.doubleValue())
206+
cell.setCellValue(((Number)value).doubleValue())
204207
} else if (value instanceof Boolean) {
205-
cell.setCellValue(value)
208+
cell.setCellValue((Boolean)value)
206209
} else {
207210
cell.setCellValue(value.toString())
208211
}
@@ -287,4 +290,38 @@ abstract class CreatesCells {
287290
}
288291
}
289292

293+
protected void performMerge(Map style, Closure callable) {
294+
Map existingDefaultOptions = defaultOptions
295+
296+
if (style != null && !style.isEmpty()) {
297+
Map newDefaultOptions = new LinkedHashMap(style)
298+
styleBuilder.convertSimpleOptions(newDefaultOptions)
299+
newDefaultOptions = styleBuilder.merge(defaultOptions, newDefaultOptions)
300+
defaultOptions = newDefaultOptions
301+
}
302+
303+
Map borderOptions = defaultOptions?.containsKey('border') ? (Map)defaultOptions.remove('border') : Collections.emptyMap()
304+
305+
callable.resolveStrategy = Closure.DELEGATE_FIRST
306+
callable.delegate = this
307+
int startingIndex = mergeIndex
308+
callable.call()
309+
int endingIndex = mergeIndex - 1
310+
if (endingIndex > startingIndex) {
311+
CellRangeAddress range = getRange(startingIndex, endingIndex)
312+
sheet.addMergedRegion(range)
313+
if (!borderOptions.isEmpty()) {
314+
styleBuilder.applyBorderToRegion(getBorderStyleApplier(range, sheet), borderOptions)
315+
}
316+
}
317+
318+
defaultOptions = existingDefaultOptions
319+
}
320+
321+
protected abstract CellRangeAddress getRange(int start, int end)
322+
323+
protected abstract int getMergeIndex()
324+
325+
protected abstract CellRangeBorderStyleApplier getBorderStyleApplier(CellRangeAddress range, XSSFSheet sheet)
326+
290327
}

src/main/groovy/com/jameskleeh/excel/style/BorderStyleApplier.groovy

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import org.apache.poi.xssf.usermodel.XSSFColor
55
import org.apache.poi.xssf.usermodel.extensions.XSSFCellBorder.BorderSide
66

77
/**
8-
* Created by jameskleeh on 7/3/17.
8+
* An interface used to apply colors and styles to borders
9+
*
10+
* @author James Kleeh
911
*/
1012
interface BorderStyleApplier {
1113

@@ -16,4 +18,4 @@ interface BorderStyleApplier {
1618
void applyColor(BorderSide side, XSSFColor color)
1719

1820
void applyColor(XSSFColor color)
19-
}
21+
}

0 commit comments

Comments
 (0)