<commit_before>package com.liangdp.graphviz4s

import java.io.File
import java.io.PrintWriter
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.Map

/**
 * Assemble, save, and render DOT source code, open result in viewer.
 * @author <NAME>
 */
abstract class Dot(
  var name: String = null, var comment: String = null,
  var strict: Boolean = false,
  var graphAttr: Map[String, String] = Map[String, String](),
  var nodeAttr: Map[String, String] = Map[String, String](),
  var edgeAttr: Map[String, String] = Map[String, String](),
  val body: ArrayBuffer[String] = ArrayBuffer[String]()) {

  protected def _head: String
  protected def _edge: String // = "\t\t%s -> %s %s"
  protected def _edgePlain: String

  protected val _comment = "// %s"
  protected val _subgraph = "subgraph %s{"
  protected val _node = "\t%s %s"
  protected val _tail = "}"

  protected val HTML_STRING =
    """</?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[\^'">\s]+))?)+\s*|\s*)/?>""".r

  /**
   * Return DOT identifier from string, quote if needed.
   */
  protected def quote(identifier: String): String = {
    if (HTML_STRING.findAllMatchIn(identifier).length <= 0) {
      var result = identifier
      if (result(0) != '"') result = s""""$result"""
      if (result(result.length - 1) != '"') result = s"""$result""""
      result
    } else identifier
  }

  /**
   * Return assembled DOT attributes string.
   */
  protected def attribute(label: String = null, attrs: Map[String, String] = null): String = {
    val tmpAttr = if (attrs == null) Map[String, String]() else attrs
    if (label != null) {
      s"""[label=${quote(label)} """ +
        s"""${("" /: tmpAttr){ (acc, elem) => s"$acc ${elem._1}=${elem._2}"}}]"""
    }
    else {
      s"""[${("" /: tmpAttr){ (acc, elem) => s"$acc ${elem._1}=${elem._2}"}}]"""
    }
  }

  /**
   * Create a node.
   * @param name Unique identifier for the node inside the source.
   * @param label Caption to be displayed (defaults to the node name).
   * @param attrs Any additional node attributes (must be strings).
   */
  def node(name: String, label: String = null, attrs: Map[String, String] = null): Unit = {
    this.body += _node.format(quote(name), attribute(label, attrs))
  }

  /**
   * Create an edge between two nodes.
   * @param tailName Start node identifier.
   * @param headName End node identifier.
   * @param label Caption to be displayed near the edge.
   * @param attrs Any additional edge attributes (must be strings).
   */
  def edge(tailName: String, headName: String,
      label: String = null, attrs: Map[String, String] = null): Unit = {
    this.body += _edge.format(tailName, headName, attribute(label, attrs))
  }

/**
 * Create a bunch of edges.
 * @param tailHeads array of (tailName, headName) pairs.
 */
  def edges(tailHeads: Array[(String, String)]): Unit = {
    for ((t, h) <- tailHeads) {
      this.body += this._edgePlain.format(t, h)
    }
  }

  /**<commit_msg>add methods<commit_after>package com.liangdp.graphviz4s

import java.io.File
import java.io.PrintWriter
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.Map

/**
 * Assemble, save, and render DOT source code, open result in viewer.
 * @author <NAME>
 */
abstract class Dot(
  var name: String = null, var comment: String = null,
  var strict: Boolean = false,
  var graphAttr: Map[String, String] = Map[String, String](),
  var nodeAttr: Map[String, String] = Map[String, String](),
  var edgeAttr: Map[String, String] = Map[String, String](),
  val body: ArrayBuffer[String] = ArrayBuffer[String]()) {

  protected def _head: String
  protected def _edge: String // = "\t\t%s -> %s %s"
  protected def _edgePlain: String

  protected val _comment = "// %s"
  protected val _subgraph = "subgraph %s{"
  protected val _node = "\t%s %s"
  protected val _tail = "}"

  protected val HTML_STRING =
    """</?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[\^'">\s]+))?)+\s*|\s*)/?>""".r

  /**
   * Return DOT identifier from string, quote if needed.
   */
  protected def quote(identifier: String): String = {
    if (HTML_STRING.findAllMatchIn(identifier).length <= 0) {
      var result = identifier
      if (result(0) != '"') result = s""""$result"""
      if (result(result.length - 1) != '"') result = s"""$result""""
      result
    } else identifier
  }

  /**
   * Return assembled DOT attributes string.
   */
  protected def attribute(label: String = null, attrs: Map[String, String] = null): String = {
    val tmpAttr = if (attrs == null) Map[String, String]() else attrs
    if (label != null) {
      s"""[label=${quote(label)} """ +
        s"""${("" /: tmpAttr){ (acc, elem) => s"$acc ${elem._1}=${elem._2}"}}]"""
    }
    else {
      s"""[${("" /: tmpAttr){ (acc, elem) => s"$acc ${elem._1}=${elem._2}"}}]"""
    }
  }

  /**
   * Create a node.
   * @param name Unique identifier for the node inside the source.
   * @param label Caption to be displayed (defaults to the node name).
   * @param attrs Any additional node attributes (must be strings).
   */
  def node(name: String, label: String = null, attrs: Map[String, String] = null): Unit = {
    this.body += _node.format(quote(name), attribute(label, attrs))
  }

  /**
   * Create an edge between two nodes.
   * @param tailName Start node identifier.
   * @param headName End node identifier.
   * @param label Caption to be displayed near the edge.
   * @param attrs Any additional edge attributes (must be strings).
   */
  def edge(tailName: String, headName: String,
      label: String = null, attrs: Map[String, String] = null): Unit = {
    this.body += _edge.format(tailName, headName, attribute(label, attrs))
  }

  /**
   * Create a bunch of edges.
   * @param tailName Start node identifier.
   * @param headNames End nodes identifier.
   */
  def edges(tailName: String, headNames: Array[String]): Unit = {
    edges(headNames.map(h => (tailName, h)))
  }

/**
 * Create a bunch of edges.
 * @param tailHeads array of (tailName, headName) pairs.
 */
  def edges(tailHeads: Array[(String, String)]): Unit = {
    for ((t, h) <- tailHeads) {
      this.body += this._edgePlain.format(t, h)
    }
  }

  /**
{'fraction_non_alphanumeric': 0.1713082901554404, 'fraction_numerical': 0.0025906735751295338, 'mean_word_length': 3.271784232365145, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1782278', 'n_tokens_mistral': 2021, 'n_tokens_neox': 2002, 'n_words': 609}