Skip to content

Commit 81573c5

Browse files
committed
Add link support
1 parent 3242088 commit 81573c5

File tree

9 files changed

+193
-29
lines changed

9 files changed

+193
-29
lines changed

src/main/groovy/com/jameskleeh/excel/Formula.groovy renamed to src/main/groovy/com/jameskleeh/excel/CellFinder.groovy

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@ import org.apache.poi.ss.util.CellReference
2323
import org.apache.poi.xssf.usermodel.XSSFCell
2424

2525
/**
26-
* A class to get references to cells for use in formulas
26+
* A class to get references to cells for use in other functions
2727
*
2828
* @author James Kleeh
2929
* @since 0.1.0
3030
*/
3131
@CompileStatic
32-
class Formula {
32+
class CellFinder {
3333

3434
private final XSSFCell cell
3535
private final Map<Object, Integer> columnIndexes
3636

37-
Formula(XSSFCell cell, Map<Object, Integer> columnIndexes) {
37+
CellFinder(XSSFCell cell, Map<Object, Integer> columnIndexes) {
3838
this.cell = cell
3939
this.columnIndexes = columnIndexes
4040
}
@@ -132,4 +132,11 @@ class Formula {
132132
String exactCell(String columnName) {
133133
exactCell(columnName, 0)
134134
}
135+
136+
/**
137+
* @return The current sheet name
138+
*/
139+
String getSheetName() {
140+
cell.sheet.sheetName
141+
}
135142
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ class Column extends CreatesCells {
3939
private int columnIdx
4040
private int rowIdx
4141

42-
Column(XSSFWorkbook workbook, XSSFSheet sheet, Map defaultOptions, Map<Object, Integer> columnIndexes, CellStyleBuilder styleBuilder, int columnIdx, int rowIdx) {
43-
super(workbook, sheet, defaultOptions, columnIndexes, styleBuilder)
42+
Column(XSSFSheet sheet, Map defaultOptions, Map<Object, Integer> columnIndexes, CellStyleBuilder styleBuilder, int columnIdx, int rowIdx) {
43+
44+
super(sheet, defaultOptions, columnIndexes, styleBuilder)
4445
this.columnIdx = columnIdx
4546
this.rowIdx = rowIdx
4647
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ class Row extends CreatesCells {
3939

4040
private int cellIdx
4141

42-
Row(XSSFWorkbook workbook, XSSFSheet sheet, XSSFRow row, Map defaultOptions, Map<Object, Integer> columnIndexes, CellStyleBuilder styleBuilder) {
43-
super(workbook, sheet, defaultOptions, columnIndexes, styleBuilder)
42+
Row(XSSFRow row, Map defaultOptions, Map<Object, Integer> columnIndexes, CellStyleBuilder styleBuilder) {
43+
super(row.sheet, defaultOptions, columnIndexes, styleBuilder)
4444
this.row = row
4545
this.cellIdx = 0
4646
}

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ package com.jameskleeh.excel
2121
import groovy.transform.CompileStatic
2222
import org.apache.poi.xssf.usermodel.XSSFRow
2323
import org.apache.poi.xssf.usermodel.XSSFSheet
24-
import org.apache.poi.xssf.usermodel.XSSFWorkbook
2524

2625
/**
2726
* A class used to create a sheet in an excel document
@@ -33,7 +32,6 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook
3332
class Sheet {
3433

3534
private final XSSFSheet sheet
36-
private final XSSFWorkbook workbook
3735
private int rowIdx
3836
private int columnIdx
3937
private Map defaultOptions
@@ -42,8 +40,7 @@ class Sheet {
4240

4341
private static final String HEIGHT = 'height'
4442

45-
Sheet(XSSFWorkbook workbook, XSSFSheet sheet, CellStyleBuilder styleBuilder) {
46-
this.workbook = workbook
43+
Sheet(XSSFSheet sheet, CellStyleBuilder styleBuilder) {
4744
this.sheet = sheet
4845
this.rowIdx = 0
4946
this.columnIdx = 0
@@ -95,7 +92,7 @@ class Sheet {
9592
*/
9693
void column(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Column) Closure callable) {
9794
callable.resolveStrategy = Closure.DELEGATE_FIRST
98-
callable.delegate = new Column(workbook, sheet, defaultOptions, columnIndexes, styleBuilder, columnIdx, rowIdx)
95+
callable.delegate = new Column(sheet, defaultOptions, columnIndexes, styleBuilder, columnIdx, rowIdx)
9996
callable.call()
10097
columnIdx++
10198
}
@@ -155,7 +152,7 @@ class Sheet {
155152

156153
if (callable != null) {
157154
callable.resolveStrategy = Closure.DELEGATE_FIRST
158-
callable.delegate = new Row(workbook, sheet, row, defaultOptions, columnIndexes, styleBuilder)
155+
callable.delegate = new Row(row, defaultOptions, columnIndexes, styleBuilder)
159156
if (callable.maximumNumberOfParameters == 1) {
160157
callable.call(row)
161158
} else {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class Workbook {
109109
}
110110
}
111111

112-
callable.delegate = new Sheet(wb, sheet, styleBuilder)
112+
callable.delegate = new Sheet(sheet, styleBuilder)
113113
if (callable.maximumNumberOfParameters == 1) {
114114
callable.call(sheet)
115115
} else {

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

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@ package com.jameskleeh.excel.internal
2020

2121
import com.jameskleeh.excel.CellStyleBuilder
2222
import com.jameskleeh.excel.Excel
23-
import com.jameskleeh.excel.Formula
23+
import com.jameskleeh.excel.Font
24+
import com.jameskleeh.excel.CellFinder
25+
import org.apache.poi.common.usermodel.Hyperlink
2426
import org.apache.poi.xssf.usermodel.XSSFCell
27+
import org.apache.poi.xssf.usermodel.XSSFHyperlink
2528
import org.apache.poi.xssf.usermodel.XSSFSheet
2629
import org.apache.poi.xssf.usermodel.XSSFWorkbook
2730

31+
import java.awt.Color
32+
2833
/**
2934
* A base class used to create cells
3035
*
@@ -37,9 +42,10 @@ abstract class CreatesCells {
3742
protected Map defaultOptions
3843
protected final Map<Object, Integer> columnIndexes
3944
protected final CellStyleBuilder styleBuilder
45+
protected static final Map LINK_OPTIONS = [font: [style: Font.UNDERLINE, color: Color.BLUE]]
4046

41-
CreatesCells(XSSFWorkbook workbook, XSSFSheet sheet, Map defaultOptions, Map<Object, Integer> columnIndexes, CellStyleBuilder styleBuilder) {
42-
this.workbook = workbook
47+
CreatesCells(XSSFSheet sheet, Map defaultOptions, Map<Object, Integer> columnIndexes, CellStyleBuilder styleBuilder) {
48+
this.workbook = sheet.workbook
4349
this.sheet = sheet
4450
this.defaultOptions = defaultOptions
4551
this.columnIndexes = columnIndexes
@@ -121,7 +127,7 @@ abstract class CreatesCells {
121127
* @param callable The return value will be assigned to the cell formula. The closure delegate contains helper methods to get references to other cells.
122128
* @return The native cell
123129
*/
124-
XSSFCell formula(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Formula) Closure callable) {
130+
XSSFCell formula(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = CellFinder) Closure callable) {
125131
formula(null, callable)
126132
}
127133

@@ -132,10 +138,10 @@ abstract class CreatesCells {
132138
* @param callable The return value will be assigned to the cell formula. The closure delegate contains helper methods to get references to other cells.
133139
* @return The native cell
134140
*/
135-
XSSFCell formula(final Map style, @DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Formula) Closure callable) {
141+
XSSFCell formula(final Map style, @DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = CellFinder) Closure callable) {
136142
XSSFCell cell = nextCell()
137143
callable.resolveStrategy = Closure.DELEGATE_FIRST
138-
callable.delegate = new Formula(cell, columnIndexes)
144+
callable.delegate = new CellFinder(cell, columnIndexes)
139145
String formula
140146
if (callable.maximumNumberOfParameters == 1) {
141147
formula = (String)callable.call(cell)
@@ -201,6 +207,40 @@ abstract class CreatesCells {
201207
cell
202208
}
203209

210+
protected XSSFCell handleLink(XSSFCell cell, String address, int linkType) {
211+
XSSFHyperlink link = workbook.creationHelper.createHyperlink(linkType)
212+
link.address = address
213+
cell.hyperlink = link
214+
cell
215+
}
216+
217+
/**
218+
* Creates a cell with a hyperlink
219+
*
220+
* @param value The cell value
221+
* @param address The link address
222+
* @param linkType The type of link. One of {@link Hyperlink#LINK_URL}, {@link Hyperlink#LINK_EMAIL}, {@link Hyperlink#LINK_FILE}
223+
* @return The native cell
224+
*/
225+
XSSFCell link(Object value, String address, int linkType) {
226+
XSSFCell cell = cell(value, LINK_OPTIONS)
227+
handleLink(cell, address, linkType)
228+
}
229+
230+
/**
231+
* Creates a cell with a hyperlink to another cell in the document
232+
*
233+
* @param value The cell value
234+
* @param callable The closure to build the link
235+
* @return The native cell
236+
*/
237+
XSSFCell link(Object value, @DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = CellFinder) Closure callable) {
238+
XSSFCell cell = cell(value, LINK_OPTIONS)
239+
callable.resolveStrategy = Closure.DELEGATE_FIRST
240+
callable.delegate = new CellFinder(cell, columnIndexes)
241+
handleLink(cell, callable.call().toString(), Hyperlink.LINK_DOCUMENT)
242+
}
243+
204244
/**
205245
* Merges cells
206246
*
@@ -230,4 +270,4 @@ abstract class CreatesCells {
230270
}
231271
}
232272

233-
}
273+
}

src/test/groovy/com/jameskleeh/excel/CellStyleBuilderSpec.groovy

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,24 @@ class CellStyleBuilderSpec extends Specification {
196196

197197
then:
198198
thrown(IllegalArgumentException)
199+
200+
when:
201+
cellStyle = cellStyleBuilder.buildStyle('', [font: [size: 13]])
202+
203+
then:
204+
cellStyle.font.fontHeight == (short)260
205+
206+
when:
207+
cellStyle = cellStyleBuilder.buildStyle('', [font: [size: 12.5]])
208+
209+
then:
210+
cellStyle.font.fontHeight == (short)250
211+
212+
when:
213+
cellStyle = cellStyleBuilder.buildStyle('', [font: [name: 'Arial']])
214+
215+
then:
216+
cellStyle.font.fontName == 'Arial'
199217
}
200218

201219
void "test buildStyle hidden"() {
@@ -595,7 +613,7 @@ class CellStyleBuilderSpec extends Specification {
595613
style.borderBottomEnum == BorderStyle.DASH_DOT
596614
}
597615

598-
void "test merging of options"() {
616+
void "test merging of options with rows"() {
599617
given:
600618
XSSFCell testCell1
601619
XSSFCell testCell2
@@ -652,5 +670,61 @@ class CellStyleBuilderSpec extends Specification {
652670
style4.borderBottomEnum == BorderStyle.NONE
653671
}
654672

673+
void "test merging of options with columns"() {
674+
given:
675+
XSSFCell testCell1
676+
XSSFCell testCell2
677+
XSSFCell testCell3
678+
XSSFCell testCell4
679+
ExcelBuilder.build {
680+
sheet {
681+
defaultStyle([border: BorderStyle.MEDIUM])
682+
column {
683+
defaultStyle([border: [left: BorderStyle.HAIR]])
684+
merge([border: [right: BorderStyle.THICK], alignment: 'center']) {
685+
testCell1 = cell('Foo')
686+
cell('')
687+
cell('')
688+
cell('')
689+
cell('')
690+
cell('')
691+
}
692+
testCell2 = cell('')
693+
}
694+
column {
695+
testCell3 = cell('')
696+
}
697+
}
698+
sheet {
699+
column {
700+
testCell4 = cell('')
701+
}
702+
}
703+
}
704+
705+
when:
706+
XSSFCellStyle style1 = testCell1.cellStyle //sheet, row, merge
707+
XSSFCellStyle style2 = testCell2.cellStyle //sheet, row
708+
XSSFCellStyle style3 = testCell3.cellStyle //sheet
709+
XSSFCellStyle style4 = testCell4.cellStyle //none
710+
711+
then:
712+
style1.borderLeftEnum == BorderStyle.HAIR
713+
style1.borderTopEnum == BorderStyle.MEDIUM
714+
style1.borderRightEnum == BorderStyle.THICK
715+
style1.borderBottomEnum == BorderStyle.MEDIUM
716+
style2.borderLeftEnum == BorderStyle.HAIR
717+
style2.borderTopEnum == BorderStyle.MEDIUM
718+
style2.borderRightEnum == BorderStyle.MEDIUM
719+
style2.borderBottomEnum == BorderStyle.MEDIUM
720+
style3.borderLeftEnum == BorderStyle.MEDIUM
721+
style3.borderTopEnum == BorderStyle.MEDIUM
722+
style3.borderRightEnum == BorderStyle.MEDIUM
723+
style3.borderBottomEnum == BorderStyle.MEDIUM
724+
style4.borderLeftEnum == BorderStyle.NONE
725+
style4.borderTopEnum == BorderStyle.NONE
726+
style4.borderRightEnum == BorderStyle.NONE
727+
style4.borderBottomEnum == BorderStyle.NONE
728+
}
655729

656730
}

src/test/groovy/com/jameskleeh/excel/ColumnSpec.groovy

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.jameskleeh.excel
22

3+
import org.apache.poi.common.usermodel.Hyperlink
4+
import org.apache.poi.ss.usermodel.Cell
35
import org.apache.poi.ss.util.CellRangeAddress
6+
import org.apache.poi.xssf.usermodel.XSSFRow
47
import org.apache.poi.xssf.usermodel.XSSFSheet
58
import org.apache.poi.xssf.usermodel.XSSFWorkbook
69
import spock.lang.Specification
@@ -86,4 +89,37 @@ class ColumnSpec extends Specification {
8689
sheet.getRow(4).getCell(0).getStringCellValue() == 'A5'
8790
sheet.getRow(5).getCell(0).getStringCellValue() == 'A6'
8891
}
92+
93+
void "test link"() {
94+
XSSFWorkbook workbook = ExcelBuilder.build {
95+
sheet("X") {
96+
row {
97+
link('Test URL', 'http://www.google.com', Hyperlink.LINK_URL)
98+
link('Test File', 'test.docx', Hyperlink.LINK_FILE)
99+
link('Test Email', 'mailto:[email protected]', Hyperlink.LINK_EMAIL)
100+
link('Test Document') {
101+
"'${getSheetName()}'!${exactCell(1,1)}"
102+
}
103+
}
104+
}
105+
}
106+
107+
when:
108+
XSSFRow row = workbook.getSheetAt(0).getRow(0)
109+
List<Cell> cells = row.cellIterator().toList()
110+
111+
then:
112+
cells[0].stringCellValue == 'Test URL'
113+
cells[0].hyperlink.address == 'http://www.google.com'
114+
cells[0].hyperlink.type == Hyperlink.LINK_URL
115+
cells[1].stringCellValue == 'Test File'
116+
cells[1].hyperlink.address == 'test.docx'
117+
cells[1].hyperlink.type == Hyperlink.LINK_FILE
118+
cells[2].stringCellValue == 'Test Email'
119+
cells[2].hyperlink.address == 'mailto:[email protected]'
120+
cells[2].hyperlink.type == Hyperlink.LINK_EMAIL
121+
cells[3].stringCellValue == 'Test Document'
122+
cells[3].hyperlink.address == "'X'!B2"
123+
cells[3].hyperlink.type == Hyperlink.LINK_DOCUMENT
124+
}
89125
}

0 commit comments

Comments
 (0)