diff --git a/build.gradle b/build.gradle index d550c8e..b7325cb 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ plugins { id 'org.asciidoctor.gradle.asciidoctor' version '1.5.1' } -String currentVersion = '0.1.11' +String currentVersion = '0.1.12' version = currentVersion diff --git a/spreadsheet-builder-api/src/main/java/org/modelcatalogue/builder/spreadsheet/api/HasStyle.java b/spreadsheet-builder-api/src/main/java/org/modelcatalogue/builder/spreadsheet/api/HasStyle.java index f292756..2b74419 100644 --- a/spreadsheet-builder-api/src/main/java/org/modelcatalogue/builder/spreadsheet/api/HasStyle.java +++ b/spreadsheet-builder-api/src/main/java/org/modelcatalogue/builder/spreadsheet/api/HasStyle.java @@ -6,6 +6,27 @@ import groovy.transform.stc.FromString; public interface HasStyle { + + /** + * Applies a customized named style to the current element. + * + * @param name the name of the style + * @param styleDefinition the definition of the style customizing the predefined style + */ + void style(String name, @DelegatesTo(CellStyle.class) @ClosureParams(value=FromString.class, options = "org.modelcatalogue.builder.spreadsheet.api.CellStyle") Closure styleDefinition); + + /** + * Applies the style defined by the closure to the current element. + * @param styleDefinition the definition of the style + */ void style(@DelegatesTo(CellStyle.class) @ClosureParams(value=FromString.class, options = "org.modelcatalogue.builder.spreadsheet.api.CellStyle") Closure styleDefinition); + + /** + * Applies the names style to the current element. + * + * The style can be changed no longer. + * + * @param name the name of the style + */ void style(String name); } diff --git a/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiCell.groovy b/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiCell.groovy index 2b790dc..2aff1a7 100644 --- a/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiCell.groovy +++ b/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiCell.groovy @@ -119,7 +119,17 @@ class PoiCell extends AbstractCell implements Resolvable { @Override void style(String name) { - style row.sheet.workbook.getStyle(name) + if (!poiCellStyle) { + poiCellStyle = row.sheet.workbook.getStyle(name) + return + } + poiCellStyle.with row.sheet.workbook.getStyleDefinition(name) + } + + @Override + void style(String name, @ClosureParams(value=FromString.class, options = "org.modelcatalogue.builder.spreadsheet.api.CellStyle") Closure styleDefinition) { + style row.sheet.workbook.getStyleDefinition(name) + style styleDefinition } @Override diff --git a/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiCellStyle.groovy b/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiCellStyle.groovy index fe035e6..2fbc87c 100644 --- a/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiCellStyle.groovy +++ b/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiCellStyle.groovy @@ -26,28 +26,43 @@ import java.util.regex.Matcher class PoiCellStyle extends AbstractCellStyle { private final XSSFCellStyle style - private final PoiCell cell + private final PoiWorkbook workbook private PoiFont poiFont private PoiBorder poiBorder + private boolean sealed + PoiCellStyle(PoiCell cell) { - this.cell = cell if (cell.cell.cellStyle == cell.cell.sheet.workbook.stylesSource.getStyleAt(0)) { style = cell.cell.sheet.workbook.createCellStyle() as XSSFCellStyle cell.cell.cellStyle = style } else { style = cell.cell.cellStyle as XSSFCellStyle } + workbook = cell.row.sheet.workbook + } + + PoiCellStyle(PoiWorkbook workbook) { + this.style = workbook.workbook.createCellStyle() as XSSFCellStyle + this.workbook = workbook } @Override void base(String stylename) { - with cell.row.sheet.workbook.getStyle(stylename) + checkSealed() + with workbook.getStyleDefinition(stylename) + } + + void checkSealed() { + if (sealed) { + throw new IllegalStateException("The cell style is already sealed! You need to create new style.") + } } @Override void background(String hexColor) { + checkSealed() if (style.fillForegroundColor == IndexedColors.AUTOMATIC.index) { foreground hexColor } else { @@ -57,11 +72,13 @@ class PoiCellStyle extends AbstractCellStyle { @Override void background(Color colorPreset) { + checkSealed() background colorPreset.hex } @Override void foreground(String hexColor) { + checkSealed() if (style.fillForegroundColor != IndexedColors.AUTOMATIC.index) { // already set as background color style.setFillBackgroundColor(style.getFillForegroundXSSFColor()) @@ -74,11 +91,13 @@ class PoiCellStyle extends AbstractCellStyle { @Override void foreground(Color colorPreset) { + checkSealed() foreground colorPreset.hex } @Override void fill(ForegroundFill fill) { + checkSealed() switch (fill) { case ForegroundFill.NO_FILL: style.fillPattern = CellStyle.NO_FILL @@ -136,43 +155,50 @@ class PoiCellStyle extends AbstractCellStyle { @Override void font(@DelegatesTo(Font.class) @ClosureParams(value=FromString.class, options = "org.modelcatalogue.builder.spreadsheet.api.Font") Closure fontConfiguration) { + checkSealed() if (!poiFont) { - poiFont = new PoiFont(cell.row.sheet.sheet.workbook, style) + poiFont = new PoiFont(workbook.workbook, style) } poiFont.with fontConfiguration } @Override void indent(int indent) { + checkSealed() style.indention = (short) indent } Object getLocked() { + checkSealed() style.locked = true } @Override void wrap(TextKeyword text) { + checkSealed() style.wrapText = true } Object getHidden() { + checkSealed() style.hidden = true } @Override void rotation(int rotation) { + checkSealed() style.rotation = (short) rotation } @Override void format(String format) { - XSSFDataFormat dataFormat = cell.row.sheet.sheet.workbook.createDataFormat() + XSSFDataFormat dataFormat = workbook.workbook.createDataFormat() style.dataFormat = dataFormat.getFormat(format) } @Override HorizontalAlignmentConfigurer align(VerticalAlignment alignment) { + checkSealed() switch (alignment) { case PureVerticalAlignment.CENTER: style.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER) @@ -197,6 +223,7 @@ class PoiCellStyle extends AbstractCellStyle { @Override void border(@DelegatesTo(Border.class) @ClosureParams(value=FromString.class, options = "org.modelcatalogue.builder.spreadsheet.api.Border") Closure borderConfiguration) { + checkSealed() PoiBorder poiBorder = findOrCreateBorder() poiBorder.with borderConfiguration @@ -207,6 +234,7 @@ class PoiCellStyle extends AbstractCellStyle { @Override void border(BorderSide location, @DelegatesTo(Border.class) @ClosureParams(value=FromString.class, options = "org.modelcatalogue.builder.spreadsheet.api.Border") Closure borderConfiguration) { + checkSealed() PoiBorder poiBorder = findOrCreateBorder() poiBorder.with borderConfiguration poiBorder.applyTo(location) @@ -215,7 +243,7 @@ class PoiCellStyle extends AbstractCellStyle { @Override void border(BorderSide first, BorderSide second, @DelegatesTo(Border.class) @ClosureParams(value=FromString.class, options = "org.modelcatalogue.builder.spreadsheet.api.Border") Closure borderConfiguration) { - + checkSealed() PoiBorder poiBorder = findOrCreateBorder() poiBorder.with borderConfiguration poiBorder.applyTo(first) @@ -226,7 +254,7 @@ class PoiCellStyle extends AbstractCellStyle { @Override void border(BorderSide first, BorderSide second, BorderSide third, @DelegatesTo(Border.class) @ClosureParams(value=FromString.class, options = "org.modelcatalogue.builder.spreadsheet.api.Border") Closure borderConfiguration) { - + checkSealed() PoiBorder poiBorder = findOrCreateBorder() poiBorder.with borderConfiguration poiBorder.applyTo(first) @@ -262,4 +290,8 @@ class PoiCellStyle extends AbstractCellStyle { new XSSFColor([red, green, blue] as byte[]) } + + void seal() { + this.sealed = true + } } diff --git a/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiRow.groovy b/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiRow.groovy index fce41fb..5babcec 100644 --- a/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiRow.groovy +++ b/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiRow.groovy @@ -15,7 +15,7 @@ class PoiRow implements Row { private final PoiSheet sheet private String styleName - private Closure styleDefinition = { return null } + private Closure styleDefinition private final List startPositions = [] private int nextColNumber = 0 @@ -36,9 +36,15 @@ class PoiRow implements Row { PoiCell poiCell = new PoiCell(this, xssfCell) if (styleName) { - poiCell.style styleName + if (styleDefinition) { + poiCell.style styleName, styleDefinition + } else { + poiCell.style styleName + } + } else if(styleDefinition) { + poiCell.style styleDefinition } - poiCell.style styleDefinition + poiCell.value value poiCell.resolve() @@ -49,10 +55,17 @@ class PoiRow implements Row { XSSFCell xssfCell = xssfRow.createCell(nextColNumber) PoiCell poiCell = new PoiCell(this, xssfCell) + if (styleName) { - poiCell.style styleName + if (styleDefinition) { + poiCell.style styleName, styleDefinition + } else { + poiCell.style styleName + } + } else if (styleDefinition) { + poiCell.style styleDefinition } - poiCell.style styleDefinition + poiCell.with cellDefinition nextColNumber += poiCell.colspan @@ -80,10 +93,17 @@ class PoiRow implements Row { nextColNumber = column PoiCell poiCell = new PoiCell(this, xssfCell) + if (styleName) { - poiCell.style styleName + if (styleDefinition) { + poiCell.style styleName, styleDefinition + } else { + poiCell.style styleName + } + } else if(styleDefinition) { + poiCell.style styleDefinition } - poiCell.style styleDefinition + poiCell.with cellDefinition handleSpans(xssfCell, poiCell) @@ -106,6 +126,12 @@ class PoiRow implements Row { this.styleName = name } + @Override + void style(String name, @DelegatesTo(CellStyle.class) @ClosureParams(value = FromString.class, options = "org.modelcatalogue.builder.spreadsheet.api.CellStyle") Closure styleDefinition) { + style name + style styleDefinition + } + protected PoiSheet getSheet() { return sheet } diff --git a/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiWorkbook.groovy b/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiWorkbook.groovy index c147224..e341dd5 100644 --- a/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiWorkbook.groovy +++ b/spreadsheet-builder-poi/src/main/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiWorkbook.groovy @@ -4,6 +4,7 @@ import groovy.transform.stc.ClosureParams import groovy.transform.stc.FromString import org.apache.poi.ss.util.WorkbookUtil import org.apache.poi.xssf.usermodel.XSSFCell +import org.apache.poi.xssf.usermodel.XSSFDataFormat import org.apache.poi.xssf.usermodel.XSSFSheet import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.modelcatalogue.builder.spreadsheet.api.CellStyle @@ -14,7 +15,9 @@ import org.modelcatalogue.builder.spreadsheet.api.Workbook class PoiWorkbook implements Workbook { private final XSSFWorkbook workbook - private final Map namedStyles = [:] + private final Map namedStylesDefinition = [:] + private final Map namedStyles = [:] + private final Map formats = [:] private final List toBeResolved = [] PoiWorkbook(XSSFWorkbook workbook) { @@ -33,7 +36,7 @@ class PoiWorkbook implements Workbook { @Override void style(String name, @DelegatesTo(CellStyle.class) @ClosureParams(value=FromString.class, options = "org.modelcatalogue.builder.spreadsheet.api.CellStyle") Closure styleDefinition) { - namedStyles[name] = styleDefinition + namedStylesDefinition[name] = styleDefinition } @Override @@ -46,8 +49,28 @@ class PoiWorkbook implements Workbook { stylesheet.declareStyles(this) } - protected Closure getStyle(String name) { - Closure style = namedStyles[name] + XSSFWorkbook getWorkbook() { + return workbook + } + + protected PoiCellStyle getStyle(String name) { + PoiCellStyle style = namedStyles[name] + + if (style) { + return style + } + + style = new PoiCellStyle(this) + style.with getStyleDefinition(name) + style.seal() + + namedStyles[name] = style + + return style + } + + protected Closure getStyleDefinition(String name) { + Closure style = namedStylesDefinition[name] if (!style) { throw new IllegalArgumentException("Style '$name' not defined") } diff --git a/spreadsheet-builder-poi/src/test/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiExcelBuilderSpec.groovy b/spreadsheet-builder-poi/src/test/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiExcelBuilderSpec.groovy index fd8b758..21b5c2d 100644 --- a/spreadsheet-builder-poi/src/test/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiExcelBuilderSpec.groovy +++ b/spreadsheet-builder-poi/src/test/groovy/org/modelcatalogue/builder/spreadsheet/poi/PoiExcelBuilderSpec.groovy @@ -32,7 +32,43 @@ class PoiExcelBuilderSpec extends Specification { } } + style 'h1', { + font { + bold + } + } + + style 'h2', { + font { + bold + } + } + apply MyStyles // or apply(new MyStyles()) + + sheet("many rows"){ + 20000.times{ + row { + cell { + value '1' + style 'h1' + } + cell { + value '2' + style 'h2' + } + cell { + value '3' + style 'h1' + } + cell { + value '4' + style 'h2' + } + } + } + } + sheet('Sample') { row { cell {