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

org.beangle.doc.excel.template.XlsCommentAreaBuilder.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

import org.beangle.commons.bean.Properties
import org.beangle.commons.lang.Strings
import org.beangle.doc.excel.template.Notation.*
import org.beangle.doc.excel.template.XlsCommentAreaBuilder.*
import org.beangle.doc.excel.template.directive.*
import org.beangle.doc.excel.{AreaRef, CellRef}
import org.slf4j.{Logger, LoggerFactory}

import java.util.concurrent.ConcurrentHashMap
import java.util.regex.{Matcher, Pattern}
import scala.collection.mutable

class XlsCommentAreaBuilder(val transformer: Transformer, val clearTemplateCells: Boolean = true) {
  private val logger = LoggerFactory.getLogger(classOf[XlsCommentAreaBuilder])

  /**
   * Builds a list of area objects defined by top level area markup ("jx:area")
   * containing a tree of all nested directives
   */
  def build(): collection.Seq[Area] = {
    val userAreas = new mutable.ArrayBuffer[Area]
    val commentedCells = transformer.getCommentedCells
    val allDirectives = new mutable.ArrayBuffer[DirectiveData]
    val allAreas = new mutable.ArrayBuffer[Area]
    commentedCells foreach { cellData =>
      val comment = cellData.cellComment
      val directiveDatas = buildDirectiveDatas(cellData, comment)
      directiveDatas foreach { data =>
        data.directive match {
          case _: AreaDirective =>
            val userArea = Area(data.areaRef, transformer)
            allAreas.addOne(userArea)
            userAreas.addOne(userArea)
          case _ =>
            allAreas.addAll(data.directive.areas)
            allDirectives.addOne(data)
        }
      }
    }
    for (i <- allDirectives.indices) {
      val data = allDirectives(i)
      val cmdAreaRef = data.areaRef
      val cmdAreas = data.directive.areas
      var minArea: Area = null
      val minAreas = new mutable.ArrayBuffer[Area]
      for (area <- allAreas) {
        if (!cmdAreas.contains(area) && area.getAreaRef.contains(cmdAreaRef)) {
          var belongsToNextCmd = false
          for (j <- i + 1 until allDirectives.size; if !belongsToNextCmd) {
            val nextCmd = allDirectives(j)
            if (nextCmd.directive.areas.contains(area)) {
              belongsToNextCmd = true
            }
          }
          if (!(belongsToNextCmd || (minArea != null && !minArea.getAreaRef.contains(area.getAreaRef)))) {
            if (minArea != null && minArea == area) minAreas.addOne(area)
            else {
              minArea = area
              minAreas.clear()
              minAreas.addOne(minArea)
            }
          }
        }
      }
      minAreas foreach (_.addDirective(data.areaRef, data.directive))
    }
    if (clearTemplateCells) userAreas foreach (_.clearCells())
    userAreas
  }

  private def buildDirectiveDatas(cellData: CellData, text: String): Iterable[DirectiveData] = {
    val directiveDatas = new mutable.ArrayBuffer[DirectiveData]
    val commentLines = mutable.ArraySeq.make(text.split("\\n"))

    for (commentLine <- commentLines) {
      val line = commentLine.trim
      if (isDirectiveString(line)) {
        val nameEndIndex = line.indexOf(ATTR_PREFIX, DirectivePrefox.length)
        if (nameEndIndex < 0) {
          val errMsg = "Failed to parse directive line [" + line + "]. Expected '" + ATTR_PREFIX + "' symbol."
          throw new IllegalStateException(errMsg)
        }
        val directiveName = line.substring(DirectivePrefox.length, nameEndIndex).trim
        val attrMap = buildAttrMap(line, nameEndIndex)
        val lastCellRef = attrMap.get(LAST_CELL_ATTR_NAME).orNull
        if (lastCellRef == null) {
          logger.warn("Failed to find last cell ref attribute '" + LAST_CELL_ATTR_NAME + "' for directive '" + directiveName + "' in cell " + cellData.cellRef)
        } else {
          val lastCell = CellRef(lastCellRef)
          if (Strings.isBlank(lastCell.sheetName)) lastCell.sheetName = cellData.sheetName
          var cmdAreas = buildAreas(cellData, line)
          val locationAreaRef = new AreaRef(cellData.cellRef, lastCell)
          if (cmdAreas.isEmpty) {
            cmdAreas = List(Area(locationAreaRef, transformer))
          }
          DirectiveFactory.newDirective(directiveName, attrMap, cmdAreas).foreach { d =>
            directiveDatas.addOne(DirectiveData(locationAreaRef, d))
          }
        }
      }
    }
    directiveDatas
  }

  private def buildAreas(cellData: CellData, cmdLine: String): Iterable[Area] = {
    val areas = new mutable.ArrayBuffer[Area]
    val areasAttrMatcher = AREAS_ATTR_REGEX_PATTERN.matcher(cmdLine)
    if (areasAttrMatcher.find) {
      val areasAttr = areasAttrMatcher.group
      val areaRefs = extractAreaRefs(cellData, areasAttr)
      areas ++= areaRefs.map(Area(_, transformer))
    }
    areas
  }

  private def extractAreaRefs(cellData: CellData, areasAttr: String): Iterable[AreaRef] = {
    val areaRefs = new mutable.ArrayBuffer[AreaRef]
    val areaRefMatcher = FormulaProcessor.regexAreaRefPattern.matcher(areasAttr)
    while (areaRefMatcher.find) {
      val areaRefName = areaRefMatcher.group
      val areaRef = AreaRef(areaRefName)
      if (Strings.isBlank(areaRef.sheetName)) areaRef.firstCellRef.sheetName = cellData.sheetName
      areaRefs.addOne(areaRef)
    }
    areaRefs
  }

  private def buildAttrMap(cmdLine: String, nameEndIndex: Int): collection.Map[String, String] = {
    val paramsEndIndex = cmdLine.lastIndexOf(ATTR_SUFFIX)
    if (paramsEndIndex < 0) {
      val errMsg = "Failed to parse directive line [" + cmdLine + "]. Expected '" + ATTR_SUFFIX + "' symbol."
      logger.error(errMsg)
      throw new IllegalArgumentException(errMsg)
    }
    val attrString = cmdLine.substring(nameEndIndex + 1, paramsEndIndex).trim
    CellData.parseDirectiveAttributes(attrString)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy