All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.beangle.doc.excel.schema.ExcelSchemaWriter.scala Maven / Gradle / Ivy

/*
 * Copyright (C) 2005, The Beangle Software.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */

package org.beangle.doc.excel.schema

import org.apache.poi.ss.usermodel.*
import org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType.{DECIMAL, INTEGER, TEXT_LENGTH}
import org.apache.poi.ss.util.CellRangeAddress
import org.apache.poi.xssf.usermodel.*
import org.beangle.commons.collection.Collections
import org.beangle.commons.lang.Strings

import java.io.OutputStream
import scala.collection.mutable

object ExcelSchemaWriter {

  def generate(schema: ExcelSchema, os: OutputStream): Unit = {
    val workbook = new XSSFWorkbook()
    for (esheet <- schema.sheets) {
      val sheet = workbook.createSheet(esheet.name)
      sheet.setDefaultColumnWidth(15)
      var rowIdx = 0

      // write title
      esheet.title foreach { title =>
        val cell = writeRow(sheet, title, rowIdx, esheet.columns.size)
        cell.getRow.setHeightInPoints(15)
        cell.setCellStyle(getTitleStyle(workbook))
        rowIdx += 1
      }
      // write remark
      esheet.remark foreach { remark =>
        val cell = writeRow(sheet, remark, rowIdx, esheet.columns.size)
        cell.setCellStyle(getRemarkStyle(workbook))
        rowIdx += 1
      }
      // write column remarks
      val existsColumnRemark = esheet.columns.exists(_.remark.nonEmpty)
      if (existsColumnRemark) {
        val remarkRow = sheet.createRow(rowIdx)
        val remarkStyle = getColumnRemarkStyle(workbook)
        rowIdx += 1
        esheet.columns.indices foreach { i =>
          val col = esheet.columns(i)
          val cell = writeColumnRemark(sheet, col.remark.getOrElse(""), remarkRow, i)
          cell.setCellStyle(remarkStyle)
        }
      }

      // write column(name,comment)
      val defaultStyles = Collections.newMap[String, CellStyle]
      val columnRow = sheet.createRow(rowIdx)
      sheet.createFreezePane(0, rowIdx + 1)
      val optionalStyle = getColumnTitleStyle(workbook, required = false)
      val requiredStyle = getColumnTitleStyle(workbook, required = true)

      val drawing = sheet.createDrawingPatriarch
      val dvHelper = new XSSFDataValidationHelper(sheet)
      esheet.columns.indices foreach { curColumnIdx =>
        val col = esheet.columns(curColumnIdx)
        val cell = writeColumn(sheet, col.name, columnRow, curColumnIdx, col.required)
        col.comment foreach { c =>
          val comment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, curColumnIdx, rowIdx, curColumnIdx, rowIdx))
          comment.setString(new XSSFRichTextString(c + ":" + col.dataType))
          cell.setCellComment(comment)
          comment.setVisible(false)
        }
        if (col.required) {
          cell.setCellStyle(requiredStyle)
        } else {
          cell.setCellStyle(optionalStyle)
        }

        if (null == col.datas) {
          if (col.isInt) {
            sheet.addValidationData(Constraints.asNumeric(dvHelper, col, INTEGER, rowIdx + 1, curColumnIdx))
            setDefaultStyle(sheet, defaultStyles, curColumnIdx, col.format.getOrElse("0"))
          } else if (col.isDecimal) {
            sheet.addValidationData(Constraints.asNumeric(dvHelper, col, DECIMAL, rowIdx + 1, curColumnIdx))
            setDefaultStyle(sheet, defaultStyles, curColumnIdx, col.format.getOrElse("General"))
          } else if (col.isDate) {
            sheet.addValidationData(Constraints.asDate(dvHelper, col, rowIdx + 1, curColumnIdx))
            setDefaultStyle(sheet, defaultStyles, curColumnIdx, col.format.get)
          } else if (col.isTime) {
            sheet.addValidationData(Constraints.asTime(dvHelper, col, rowIdx + 1, curColumnIdx))
            setDefaultStyle(sheet, defaultStyles, curColumnIdx, col.format.get)
          } else if (col.length.nonEmpty) {
            if (col.formular1 == "0" && col.required) {
              col.formular1 = "1"
            }
            if (col.unique) {
              sheet.addValidationData(Constraints.asUnique(dvHelper, col, rowIdx + 1, curColumnIdx))
            } else {
              sheet.addValidationData(Constraints.asNumeric(dvHelper, col, TEXT_LENGTH, rowIdx + 1, curColumnIdx))
            }
            setDefaultStyle(sheet, defaultStyles, curColumnIdx, "@")
          } else if (col.isBool) {
            sheet.addValidationData(Constraints.asBoolean(dvHelper, col, rowIdx + 1, curColumnIdx))
          } else if (null != col.refs && col.refs.nonEmpty) {
            addRefValidation(schema, sheet, dvHelper, col, rowIdx + 1, curColumnIdx)
            setDefaultStyle(sheet, defaultStyles, curColumnIdx, "@")
          }
        } else {
          var dIdx = 1
          col.datas foreach { data =>
            var dataRow = sheet.getRow(dIdx + rowIdx)
            if (null == dataRow) {
              dataRow = sheet.createRow(dIdx + rowIdx)
            }
            dIdx += 1
            dataRow.createCell(curColumnIdx).setCellValue(data)
          }
        }
      }
    }

    workbook.write(os)
    os.close()
  }

  private def setDefaultStyle(sheet: Sheet, defaults: mutable.Map[String, CellStyle], columnIdx: Int, format: String): Unit = {
    val style = defaults.getOrElseUpdate(format, {
      val s = sheet.getWorkbook.createCellStyle()
      val df = sheet.getWorkbook.createDataFormat()
      s.setDataFormat(df.getFormat(format))
      s
    })
    sheet.setDefaultColumnStyle(columnIdx, style)
  }

  private def addRefValidation(schema: ExcelSchema, sheet: Sheet, helper: XSSFDataValidationHelper,
                               col: ExcelColumn, startRowIdx: Int, columnIdx: Int): Boolean = {
    var finded = false
    schema.sheets.find(_.name != sheet.getSheetName) foreach { codeSheet =>
      var codeColIdx = 'A'.toInt
      for (c <- codeSheet.columns if !finded) {
        if (c.datas eq col.refs) {
          finded = true
        }
        codeColIdx += 1
      }
      if (finded) {
        val refColumn = (codeColIdx - 1).asInstanceOf[Char]
        val formular = codeSheet.name + "!$" + refColumn + "$2:$" + refColumn + "$" + (col.refs.size + 1) //考虑有个标题,所以+1
        val validation = Constraints.asFormular(helper, formular, col, startRowIdx, columnIdx, "请选择合适的" + col.name)
        sheet.addValidationData(validation)
      }
    }
    finded
  }

  private def writeColumnRemark(sheet: Sheet, content: String, row: Row, columnIdx: Int): Cell = {
    val cell = row.createCell(columnIdx)
    val newLines = Strings.count(content.trim(), "\n")
    if (newLines > 0) {
      val newHeight = (newLines + 1) * sheet.getDefaultRowHeightInPoints
      if (newHeight > row.getHeightInPoints) {
        row.setHeightInPoints(newHeight)
      }
    }
    cell.setCellValue(content)
    cell
  }

  private def writeColumn(sheet: Sheet, content: String, row: Row, columnIdx: Int, required: Boolean): Cell = {
    val cell = row.createCell(columnIdx)
    if (required) {
      cell.setCellValue("*" + content)
    } else {
      cell.setCellValue(content)
    }
    cell
  }

  private def writeRow(sheet: Sheet, content: String, rowIdx: Int, colSpan: Int): Cell = {
    val mergedRegion = new CellRangeAddress(rowIdx, rowIdx, 0, colSpan - 1)
    sheet.addMergedRegion(mergedRegion)

    val row = sheet.createRow(rowIdx)
    val cell = row.createCell(0)

    val newLines = Strings.count(content.trim(), "\n")
    if (newLines > 0) {
      row.setHeightInPoints((newLines + 1) * sheet.getDefaultRowHeightInPoints)
    }
    cell.setCellValue(content)
    cell
  }

  private def getRemarkStyle(wb: Workbook): CellStyle = {
    val style = wb.createCellStyle
    style.setAlignment(HorizontalAlignment.LEFT)
    style.setVerticalAlignment(VerticalAlignment.CENTER)
    style.setWrapText(true)
    style
  }

  private def getTitleStyle(wb: Workbook): CellStyle = {
    val style = wb.createCellStyle
    style.setAlignment(HorizontalAlignment.CENTER)
    style.setVerticalAlignment(VerticalAlignment.CENTER)
    style.setWrapText(true)
    val font = wb.createFont
    font.setFontHeightInPoints(15.toShort)
    font.setFontName("宋体")
    font.setItalic(false)
    font.setBold(true)
    style.setFont(font)
    style
  }

  private def getColumnRemarkStyle(wb: Workbook): CellStyle = {
    val style = wb.createCellStyle.asInstanceOf[XSSFCellStyle]
    style.setAlignment(HorizontalAlignment.CENTER)
    style.setVerticalAlignment(VerticalAlignment.CENTER)
    style.setWrapText(true)
    style.setFillForegroundColor(IndexedColors.AUTOMATIC.index)
    style
  }

  private def getColumnTitleStyle(wb: Workbook, required: Boolean): CellStyle = {
    val style = wb.createCellStyle.asInstanceOf[XSSFCellStyle]
    style.setAlignment(HorizontalAlignment.CENTER) // 左右居中
    style.setVerticalAlignment(VerticalAlignment.CENTER) // 上下居中

    style.setFillPattern(FillPatternType.SOLID_FOREGROUND)
    val rgb = Array(221.toByte, 217.toByte, 196.toByte)
    style.setFillForegroundColor(new XSSFColor(rgb, new DefaultIndexedColorMap))

    style.setBorderTop(BorderStyle.THIN)
    style.setBorderBottom(BorderStyle.THIN)
    style.setBorderLeft(BorderStyle.THIN)
    style.setBorderRight(BorderStyle.THIN)

    val font = wb.createFont.asInstanceOf[XSSFFont]
    font.setBold(true)
    if (required) {
      font.setColor(IndexedColors.RED.index)
    }
    style.setFont(font)
    style
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy