My code have too much nested option processing which makes it ugly. But it has an advantage it has no side effect and typesafe.
package repos
import java.io.{InputStream, File}
import org.apache.poi.hssf.usermodel.HSSFWorkbook
import org.apache.poi.ss.usermodel.{Cell, Row, Workbook, Sheet}
import org.apache.poi.xssf.usermodel._
import scala.collection.JavaConversions._
object ExcelRepo{
def read(binary: InputStream, filename: String) = {
val excelExtension = "([^\\s]+(\\.(?i)(xls|xlsx))$)"
val excelData = if(filename.matches(excelExtension)) {
val xlsExtension = "([^\\s]+(\\.(?i)(xls))$)"
val xlsxExtension = "([^\\s]+(\\.(?i)(xlsx))$)"
val workbook:Option[Workbook] = if (filename.matches(xlsExtension)) {
println("XLS")
Some(new HSSFWorkbook(binary))
} else {
println("XLSX")
Some(new XSSFWorkbook(binary))
}
val data = workbook match {
case Some(wb: Workbook) =>
val sheet = Option(wb.getSheetAt(0))
sheet match {
case Some(s: Sheet) =>
val titleRow = Option(s.getRow(0))
titleRow match {
case Some(tr:Row) =>
val titleCell = Option(tr.getCell(0))
val priceCell = Option(tr.getCell(1))
(titleCell, priceCell) match {
case (Some(tc: Cell), Some(pc:Cell)) =>
if (pc.getStringCellValue == "price" && tc.getStringCellValue == "title") {
val list:List[(String, Double)] = sheet.get.tail.map{
r => (r.getCell(0).getStringCellValue, r.getCell(1).getNumericCellValue)}.toList
list
} else {
List()
}
case (_,_) => List()
}
case None => List()
}
case None => List()
}
case None => List()
}
data
} else {
List()
}
excelData
}
}
This function reads Excel file assuming that it is not null, and returning the List
of tuples with (price, title), or empty list if workbook or row or cell is empty.
Is it actually ugly? Should I refactor it? If I should what is the clear and concise way to deal with match Option → getting new Option → match it again → getting new Option ...