<commit_before>/*
 * Writesetter is a program for creating PDF documents from text files with markup.
 * Copyright (c) 2013 <NAME> <<EMAIL>>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package writesetter.core

import scala.collection.mutable.Stack

class ProcessingUnit(extensions: Extensions) {

  object sourceType extends Enumeration {
    val root = Value
    val insertion = Value
    val tag = Value
    val mainTag = Value
    val inLineTag = Value
    val injection = Value
    val variable = Value
    val errorMessage = Value
  }

  private class Element(s: sourceType.Value, n: String, ln: Long) {
    val source = s // root, file, tag, mainTag, inLineTag, injection, variable, error
    val name = n // name of file, tag, inLineTag, injection, variable

    private var lineNumber = ln
    private var lineContent = ""

    def setLine(line: String) {
      lineNumber += 1
      lineContent = line
    }
    def trim() { lineContent = lineContent.trim }
    def getLine = lineContent
    def getNumber = lineNumber
  }

  private var elements = new Stack[Element]

  private var caretPostionForPreview = 0
  private var rootPosition = 0 // Compared with caret position in editor to determine initial page in preview.

  def setCaretPosition(pos: Int) { caretPostionForPreview = pos }

  def addFile(file: String, root: Boolean) {
    if (elements.exists(e => (e.source == sourceType.root || e.source == sourceType.insertion) && e.name == file)) {
      throw new TagError("Circular reference of insert file statements.")
    } else if (root) {
      elements.push(new Element(sourceType.root, file, 0))
    } else {
      elements.push(new Element(sourceType.insertion, file, 0))
    }
  }

  def addUserOrMainTag(s: sourceType.Value, tagName: String, initialLineNumber: Long) {
    if (elements.exists(e => e.source == sourceType.tag && e.name == tagName)) {
      throw new TagError("Circular references in tags defined in extensions detected for the tag '" + tagName + "'.")
    }
    elements.push(new Element(s, tagName, initialLineNumber - 1))
  }
  def addUserTag(tagName: String, initialLineNumber: Long) {
    addUserOrMainTag(sourceType.tag, tagName, initialLineNumber)
  }
  def addMainTag(tagName: String, initialLineNumber: Long) {
    addUserOrMainTag(sourceType.mainTag, tagName, initialLineNumber)
  }

  def addInLineTag(inLineTagName: String) {
    elements.push(new Element(sourceType.inLineTag, inLineTagName, 0))
  }

  def addInjection(injectionDescription: String) {
    if (elements.exists(e => e.source == sourceType.injection && e.name == injectionDescription)) {
      throw new TagError("Circular invocation of injection '" + injectionDescription + "'.")
    } else {
      elements.push(new Element(sourceType.injection, injectionDescription, 0))
    }
  }

  def addVariable(variableName: String) {
    elements.push(new Element(sourceType.variable, variableName, 0))
  }

  def addErrorMessage(message: String) {
    elements.push(new Element(sourceType.errorMessage, message, 0))
  }

  def update(line: String) {
    elements.top.setLine(line)
    if (elements.top.source == sourceType.root) rootPosition += (line.length + 1)
  }

  def isAfterCaretPositionInRoot: Boolean =
    elements.top.source == sourceType.root && rootPosition >= caretPostionForPreview

  def trim() = elements.top.trim()

  def getLine: String = elements.top.getLine

  def getReplacementPolicy: String =
    elements.top.source match {
      case sourceType.root | sourceType.insertion => "yes"
      case sourceType.tag                         => "tag"
      case _                                      => "no"
    }

  def getSourceLocation: String = {

    def nameAndLine(f: String, ln: Long): String = if (ln == 0) { f } else { f + " (line " + ln.toString + ")" }

    if (elements.top.source == sourceType.root) {
      "line " + elements.top.getNumber.toString + "."
    } else {
      var result = ""
      var index = elements.length - 1
      while (index >= 0) {
        val e = elements(index)
        if (e.source != sourceType.root) result += " / "
        e.source match {
          case sourceType.root      => result += nameAndLine(e.name, e.getNumber)
          case sourceType.insertion => result += nameAndLine(e.name, e.getNumber)
          case sourceType.tag => {
            val extensionName = extensions.TagDefinitions(e.name).extensionName
            result += "tag '" + e.name + "' in extension '" + nameAndLine(extensionName, e.getNumber) + "'"
          }
          case sourceType.mainTag => {
            val extensionName = extensions.TagDefinitions(e.name).extensionName<commit_msg>Also apply replacement to main tag, inline tag and variables if flag 't' is set.<commit_after>/*
 * Writesetter is a program for creating PDF documents from text files with markup.
 * Copyright (c) 2013 <NAME> <<EMAIL>>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package writesetter.core

import scala.collection.mutable.Stack

class ProcessingUnit(extensions: Extensions) {

  object sourceType extends Enumeration {
    val root = Value
    val insertion = Value
    val tag = Value
    val mainTag = Value
    val inLineTag = Value
    val injection = Value
    val variable = Value
    val errorMessage = Value
  }

  private class Element(s: sourceType.Value, n: String, ln: Long) {
    val source = s
    val name = n // name of file, tag, inLineTag, injection, variable

    private var lineNumber = ln
    private var lineContent = ""

    def setLine(line: String) {
      lineNumber += 1
      lineContent = line
    }
    def trim() { lineContent = lineContent.trim }
    def getLine = lineContent
    def getNumber = lineNumber
  }

  private var elements = new Stack[Element]

  private var caretPostionForPreview = 0
  private var rootPosition = 0 // Compared with caret position in editor to determine initial page in preview.

  def setCaretPosition(pos: Int) { caretPostionForPreview = pos }

  def addFile(file: String, root: Boolean) {
    if (elements.exists(e => (e.source == sourceType.root || e.source == sourceType.insertion) && e.name == file)) {
      throw new TagError("Circular reference of insert file statements.")
    } else if (root) {
      elements.push(new Element(sourceType.root, file, 0))
    } else {
      elements.push(new Element(sourceType.insertion, file, 0))
    }
  }

  def addUserOrMainTag(s: sourceType.Value, tagName: String, initialLineNumber: Long) {
    if (elements.exists(e => e.source == sourceType.tag && e.name == tagName)) {
      throw new TagError("Circular references in tags defined in extensions detected for the tag '" + tagName + "'.")
    }
    elements.push(new Element(s, tagName, initialLineNumber - 1))
  }
  def addUserTag(tagName: String, initialLineNumber: Long) {
    addUserOrMainTag(sourceType.tag, tagName, initialLineNumber)
  }
  def addMainTag(tagName: String, initialLineNumber: Long) {
    addUserOrMainTag(sourceType.mainTag, tagName, initialLineNumber)
  }

  def addInLineTag(inLineTagName: String) {
    elements.push(new Element(sourceType.inLineTag, inLineTagName, 0))
  }

  def addInjection(injectionDescription: String) {
    if (elements.exists(e => e.source == sourceType.injection && e.name == injectionDescription)) {
      throw new TagError("Circular invocation of injection '" + injectionDescription + "'.")
    } else {
      elements.push(new Element(sourceType.injection, injectionDescription, 0))
    }
  }

  def addVariable(variableName: String) {
    elements.push(new Element(sourceType.variable, variableName, 0))
  }

  def addErrorMessage(message: String) {
    elements.push(new Element(sourceType.errorMessage, message, 0))
  }

  def update(line: String) {
    elements.top.setLine(line)
    if (elements.top.source == sourceType.root) rootPosition += (line.length + 1)
  }

  def isAfterCaretPositionInRoot: Boolean =
    elements.top.source == sourceType.root && rootPosition >= caretPostionForPreview

  def trim() = elements.top.trim()

  def getLine: String = elements.top.getLine

  def getReplacementPolicy: String =
    elements.top.source match {
      case sourceType.root | sourceType.insertion => "yes"
      case sourceType.tag | sourceType.mainTag | sourceType.inLineTag | sourceType.variable => "tag"
      case _ => "no"
    }

  def getSourceLocation: String = {

    def nameAndLine(f: String, ln: Long): String = if (ln == 0) { f } else { f + " (line " + ln.toString + ")" }

    if (elements.top.source == sourceType.root) {
      "line " + elements.top.getNumber.toString + "."
    } else {
      var result = ""
      var index = elements.length - 1
      while (index >= 0) {
        val e = elements(index)
        if (e.source != sourceType.root) result += " / "
        e.source match {
          case sourceType.root      => result += nameAndLine(e.name, e.getNumber)
          case sourceType.insertion => result += nameAndLine(e.name, e.getNumber)
          case sourceType.tag => {
            val extensionName = extensions.TagDefinitions(e.name).extensionName
            result += "tag '" + e.name + "' in extension '" + nameAndLine(extensionName, e.getNumber) + "'"
          }
          case sourceType.mainTag => {
            val extensionName = extensions.TagDefinitions(e.name).extensionName
{'fraction_non_alphanumeric': 0.10433052212640956, 'fraction_numerical': 0.003600871790012319, 'mean_word_length': 3.176493866244559, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4216206', 'n_tokens_mistral': 3091, 'n_tokens_neox': 2957, 'n_words': 944}