org.beangle.doc.excel.template.directive.EachDirective.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.template.directive
import org.beangle.commons.bean.Properties
import org.beangle.commons.bean.orderings.PropertyOrdering
import org.beangle.commons.script.ExpressionEvaluator
import org.beangle.doc.excel.template.*
import org.beangle.doc.excel.template.directive.EachDirective.*
import org.beangle.doc.excel.{CellRef, Size}
import org.slf4j.LoggerFactory
import java.util
import java.util.Collection
import scala.collection.mutable
object EachDirective {
val logger = LoggerFactory.getLogger(classOf[EachDirective])
enum Direction {
case Right, Down
}
case class GroupData(item: Any, items: Iterable[_])
}
class EachDirective(var `var`: String, var items: String, var area: Area, var direction: Direction = Direction.Down) extends AbstractDirective {
private val GROUP_DATA_KEY: String = "_group"
var select: String = null
var groupBy: String = null
var groupOrder: String = null
var orderBy: String = null
var multisheet: String = null
var pageable: Boolean = false
addArea(area)
override def applyAt(cellRef: CellRef, context: Context): Size = {
var itemsCollection: Iterable[_] = null
try {
itemsCollection = transformToIterableObject(context.evaluator, items, context)
itemsCollection = orderCollection(itemsCollection)
} catch {
case e: Exception =>
logger.warn("Failed to evaluate collection expression {}", items, e)
itemsCollection = List.empty
}
var size: Size = null
if (groupBy == null || groupBy.isEmpty) {
size = processCollection(context, itemsCollection, cellRef, `var`)
} else {
val groupedData = group(itemsCollection, groupBy, groupOrder)
val groupVar = if `var` != null then `var` else GROUP_DATA_KEY
size = processCollection(context, groupedData, cellRef, groupVar)
}
if (direction == Direction.Down) area.transformer.adjustTableSize(cellRef, size)
size
}
private def orderCollection(itemsCollection: Iterable[_]): Iterable[_] = {
if (orderBy != null && orderBy.trim.nonEmpty) {
val comp = PropertyOrdering.by(orderBy)
itemsCollection.toBuffer.sorted(comp)
} else {
itemsCollection
}
}
private def group(cl: collection.Iterable[Any], groupProperty: String, groupOrder: String): Iterable[GroupData] = {
if (cl == null) return Seq.empty
val grouped = cl.groupBy(x => Properties.get[Any](x, groupProperty))
grouped.map { g =>
GroupData(g._1, g._2.toBuffer.sorted(new PropertyOrdering(groupOrder)))
}
}
/**
* Evaluates the passed collection name into an {@link Iterable} object
*
* @return an iterable object from the {@link Context} under given name
*/
private def transformToIterableObject(evaluator: ExpressionEvaluator, collectionName: String, context: Context): Iterable[_] = {
val collectionObject = evaluator.eval(collectionName, context.toMap)
collectionObject match {
case null => List.empty
case a: Array[Any] => mutable.ArraySeq(a)
case c: util.Collection[_] =>
import scala.jdk.javaapi.CollectionConverters.asScala
asScala(c)
case i: Iterable[_] => i
case _ => throw new RuntimeException(collectionName + " expression is not a collection or an array")
}
}
private def processCollection(context: Context, itemsCollection: Iterable[_], cellRef: CellRef, varName: String): Size = {
var newWidth: Int = 0
var newHeight: Int = 0
var cellRefGenerator: CellRefGenerator = null
if (multisheet != null) {
val sheetNameList = extractSheetNameList(context)
cellRefGenerator = new MultiSheetCellRefGenerator(sheetNameList, cellRef)
}
var currentCell = cellRef
val varIndex = varName + "_idx"
val currentVarObject = context.getVar(varName)
val currentVarIndexObject = context.getVar(varIndex)
var currentIndex: Int = 0
var breaked = false
var index: Int = 0
for (obj <- itemsCollection; if !breaked) {
context.putVar(varName, obj)
context.putVar(varIndex, currentIndex)
if (select != null && !context.isTrue(select)) {
context.removeVar(varName)
} else {
if (cellRefGenerator != null) {
index += 1
currentCell = cellRefGenerator.generateCellRef(index - 1, context)
}
if (currentCell == null) {
breaked = true
} else {
var size: Size = null
try size = area.applyAt(currentCell, context)
catch {
case e: NegativeArraySizeException =>
throw new RuntimeException("Check jx:each/lastCell parameter in template! Illegal area: " + area.getAreaRef, e)
}
if (cellRefGenerator == null) {
if (direction == Direction.Down) {
currentCell = new CellRef(currentCell.sheetName, currentCell.row + size.height, currentCell.col)
newWidth = Math.max(newWidth, size.width)
newHeight += size.height
} else { // RIGHT
currentCell = new CellRef(currentCell.sheetName, currentCell.row, currentCell.col + size.width)
newWidth += size.width
newHeight = Math.max(newHeight, size.height)
}
} else {
newWidth = Math.max(newWidth, size.width)
newHeight = Math.max(newHeight, size.height)
}
currentIndex += 1
if (pageable && currentIndex < itemsCollection.size) {
val sheet = area.transformer.workbook.getSheet(currentCell.sheetName)
sheet.setRowBreak(newHeight - 1) // newHeight is 1-based
}
}
}
}
restoreVarObject(context, varIndex, currentVarIndexObject)
restoreVarObject(context, varName, currentVarObject)
new Size(newWidth, newHeight)
}
private def restoreVarObject(context: Context, varName: String, varObject: Any): Unit = {
if (varName != null) {
if varObject == null then context.removeVar(varName)
else context.putVar(varName, varObject)
}
}
private def extractSheetNameList(context: Context): collection.Seq[String] = {
val sheetnames = context.getVar(multisheet).asInstanceOf[collection.Seq[String]]
require(null != sheetnames && sheetnames.nonEmpty, s"cannot find nonempty ${multisheet}")
sheetnames
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy