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

overflowdb.formats.dot.DotExporter.scala Maven / Gradle / Ivy

The newest version!
package overflowdb.formats.dot

import overflowdb.formats.{ExportResult, Exporter, iterableForList, resolveOutputFileSingle}
import overflowdb.{Edge, Node}

import java.nio.file.{Files, Path}
import scala.jdk.CollectionConverters.MapHasAsScala
import scala.util.Using

/** Exports OverflowDB Graph to graphviz dot/gv file
  *
  * Note: GraphML doesn't natively support list property types, so we fake it by encoding it as a `;` delimited string.
  * If you import this into a different database, you'll need to parse that separately.
  *
  * https://en.wikipedia.org/wiki/DOT_(graph_description_language) https://www.graphviz.org/doc/info/lang.html
  * http://magjac.com/graphviz-visual-editor/ https://www.slideshare.net/albazo/graphiz-using-the-dot-language
  */
object DotExporter extends Exporter {
  override def defaultFileExtension = "dot"

  override def runExport(nodes: IterableOnce[Node], edges: IterableOnce[Edge], outputFile: Path) = {
    val outFile = resolveOutputFileSingle(outputFile, s"export.$defaultFileExtension")
    var nodeCount, edgeCount = 0

    Using.resource(Files.newBufferedWriter(outFile)) { writer =>
      def writeLine(line: String): Unit = {
        writer.write(line)
        writer.newLine()
      }

      writeLine("digraph {")

      nodes.iterator.foreach { node =>
        nodeCount += 1
        writeLine(node2Dot(node))
      }

      edges.iterator.foreach { edge =>
        edgeCount += 1
        writeLine(edge2Dot(edge))
      }

      writeLine("}")
    }

    ExportResult(
      nodeCount = nodeCount,
      edgeCount = edgeCount,
      files = Seq(outFile),
      additionalInfo = None
    )
  }

  private def node2Dot(node: Node): String = {
    s"  ${node.id} [label=${node.label} ${properties2Dot(node.propertiesMap)}]"
  }

  private def edge2Dot(edge: Edge): String = {
    s"  ${edge.outNode.id} -> ${edge.inNode.id} [label=${edge.label} ${properties2Dot(edge.propertiesMap)}]"
  }

  private def properties2Dot(properties: java.util.Map[String, Object]): String = {
    properties.asScala
      .map { case (key, value) =>
        s"$key=${encodePropertyValue(value)}"
      }
      .mkString(" ")
  }

  private def encodePropertyValue(value: Object): String = {
    value match {
      case value: String =>
        val escaped = value
          .replace("""\""", """\\""") // escape escape chars - this should come first
          .replace("\"", "\\\"") // escape double quotes, because we use them to enclose strings
        s"\"$escaped\""
      case list if iterableForList.isDefinedAt(list) =>
        val values = iterableForList(list).mkString(";")
        s"\"$values\""
      case value => value.toString
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy