This is good stuff! Resource files are a great way to store and use static information inside a solution - and all the same parsing techniques (and largely code) can be used for the results of API calls if the data can be made available dynamically via an endpoint.
I wanted to point out some interesting “Groovy” ways to handle some of these file (or in fact any collection / stream / etc) parsing / traversing actions too
[resource].withXXX
Groovy has a load of “withXXX” methods for handling the creation and cleanup of stream based resources (think of them as a specialised implementation of Java’s “try with resources”). They function like a try ... finally { close() }
in that any resources they open will be closed / tidied when the closure returns OR throws an Exception
So the first example could remove most of the FileReader / BufferedReader and the try ... finally
and instead use withReader
:
new File(fileName).withReader('UTF-8') { reader ->
// read file content
}
Additionally the Reader
class provides some really useful closure based methods. In this case splitEachLine
seems the perfect fit!
def dataMap = [:]
new File(fileName).withReader('UTF-8') { reader ->
reader.splitEachLine(separator) { values -> {
dataMap.put(values[0], values[1])
}}
}
API calls?
Yes this works for API calls as well… assuming [endpoint path]
points to a public endpoint returning the same data, the above example would work equally well with:
"[endpoint path]".toURL().withReader('UTF-8') { reader ->
...
}
.collect { ... }
and withCloseable
The second Excel example could also be made “more Groovy” by using:
-
withCloseable {...}
which will automatically close any resource it is called on at the end / failure of the closure - similar towithReader
but called on anyjava.io.Closeable
resource (here it is used for theXSSFWorkbook
which is required to close and release the file) -
collect { ... }
which is similar to Javascriptmap()
in that it allows the same action to be called on each entry in a collection to transform each entry into a result and the results of all these calls is returned as a single array
new XSSFWorkbook(new File(fileName)).withCloseable { workbook ->
if (readSheet < 0 || readSheet >= workbook.numberOfSheets) {
throw new RuntimeException("Invalid sheet number '${readSheet}' for resource [${fileName}]. Sheet Count: ${workbook.numberOfSheets}")
}
return workbook.getSheetAt(readSheet).collect { row ->
lRelevantColumns.collect { column ->
getCellValueAsString(row.getCell(column))
}
}
}
Bonus Excel FORMULA Values!
This is very specific to Excel parsing, not at all generic Groovy and in many cases irrelevant / not desired behaviour - depending on the Excel content. However you can get the most recent formula calculation from the Excel by checking for the cached data type and returning the typed value. Note this is the value as was last time it was calculated (most likely last save) so any formulas using “fluid” data such as today’s date will not have the same value as if the file was opened in Excel
private static String getCellValueAsString(Cell cell) {
if (null != cell) {
switch (cell.cellType) {
case CellType.FORMULA: {
// When the cell is of type formula
// get the previously cached value (on save in Excel)
switch (cell.cachedFormulaResultType) {
case CellType.NUMERIC:
return cell.numericCellValue
case CellType.STRING:
return cell.stringCellValue
case CellType.BOOLEAN:
return cell.booleanCellValue
case CellType.ERROR:
return "Error: ${FormulaError.forInt(cell.errorCellValue.string)}"
}
}
case CellType.BLANK:
return ''
case CellType.ERROR:
return "Error: ${FormulaError.forInt(cell.errorCellValue.string)}"
default:
return cell // All other types the toString is the value
}
return ''
}
}