Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package flatgraph.formats.graphml
import flatgraph.formats.{ExportResult, Exporter, isList, resolveOutputFileSingle, writeFile}
import flatgraph.{Accessors, Edge, FormalQtyType, GNode, Schema}
import java.lang.System.lineSeparator
import java.nio.file.Path
import java.util.concurrent.atomic.AtomicInteger
import scala.collection.mutable
import scala.xml.{PrettyPrinter, XML}
/** Exports to GraphML
*
* Warning: list properties are not natively supported by graphml... We initially built some support for those which deviated from the
* spec, but given that other tools don't support it and the complications re re-importing, we also dropped support for lists. Now, lists
* are dropped and we print a warning.
*
* https://en.wikipedia.org/wiki/GraphML http://graphml.graphdrawing.org/primer/graphml-primer.html
*/
object GraphMLExporter extends Exporter {
override def defaultFileExtension = "xml"
override def runExport(schema: Schema, nodes: IterableOnce[GNode], edges: IterableOnce[Edge], outputFile: Path) = {
val outFile = resolveOutputFileSingle(outputFile, s"export.$defaultFileExtension")
val nodePropertyContextById = mutable.Map.empty[String, PropertyContext]
val edgePropertyContextById = mutable.Map.empty[String, PropertyContext]
val discardedListPropertyCount = new AtomicInteger(0)
val nodeEntries = nodes.iterator.map { node =>
val properties = schema.propertyKinds.flatMap { propertyKind =>
val graph = node.graph
val nodeKind = node.nodeKind
val nodeSeq = node.seq()
val valueMaybe = schema.getNodePropertyFormalQuantity(nodeKind, propertyKind) match {
case FormalQtyType.QtyNone =>
None
case FormalQtyType.QtyOne | FormalQtyType.QtyOption =>
Accessors.getNodePropertyOption[Object](graph, nodeKind, propertyKind, nodeSeq)
case FormalQtyType.QtyMulti =>
Option(Accessors.getNodePropertyMulti[Object](graph, nodeKind, propertyKind, nodeSeq)).filter(_.nonEmpty).flatMap { p =>
// as per class scaladoc: we want to skip list properties, but keep track so we can later inform the user about it...
discardedListPropertyCount.incrementAndGet()
None
}
}
valueMaybe.map { value =>
schema.getPropertyLabel(nodeKind, propertyKind) -> value
}
}
s"""
| ${node.label}
| ${dataEntries("node", node.label(), properties, nodePropertyContextById, discardedListPropertyCount)}
|
|""".stripMargin
}.toSeq
val edgeEntries = edges.iterator.map { edge =>
val property = // edges can have 0 or 1 properties
if (edge.property == null) Nil
else List("property" -> edge.property)
s"""
| ${edge.label}
| ${dataEntries("edge", edge.label, property, edgePropertyContextById, discardedListPropertyCount)}
|
|""".stripMargin
}.toSeq
def propertyKeyXml(forAttr: String, propsMap: mutable.Map[String, PropertyContext]): String = {
propsMap
.map { case (key, PropertyContext(name, tpe)) =>
s""""""
}
.mkString(lineSeparator)
}
val nodePropertyKeyEntries = propertyKeyXml("node", nodePropertyContextById)
val edgePropertyKeyEntries = propertyKeyXml("edge", edgePropertyContextById)
val xml = s"""
|
|
|
|
| $nodePropertyKeyEntries
| $edgePropertyKeyEntries
|
| ${nodeEntries.mkString(lineSeparator)}
| ${edgeEntries.mkString(lineSeparator)}
|
|
|""".stripMargin.trim
writeFile(outFile, xml)
xmlFormatInPlace(outFile)
val additionalInfo =
Some(discardedListPropertyCount.get)
.filter(_ > 0)
.map { count =>
s"warning: discarded $count list properties (because they are not supported by the graphml spec)"
}
ExportResult(nodeCount = nodeEntries.size, edgeCount = edgeEntries.size, files = Seq(outFile), additionalInfo)
}
/** warning: updates type information based on runtime instances (in mutable.Map `propertyTypeByName`) warning2: updated the
* `discardedListPropertyCount` counter - if we need to discard any list properties, display a warning to the user
*/
private def dataEntries(
prefix: String,
elementLabel: String,
properties: IterableOnce[(String, Any)],
propertyContextById: mutable.Map[String, PropertyContext],
discardedListPropertyCount: AtomicInteger
): String = {
properties.iterator
.map { case (propertyName, propertyValue) =>
if (isList(propertyValue.getClass)) {
discardedListPropertyCount.incrementAndGet()
"" // discard list properties
} else { // scalar value
val encodedPropertyName = s"${prefix}__${elementLabel}__$propertyName"
val graphMLTpe = Type.fromRuntimeClass(propertyValue.getClass)
/* update type information based on runtime instances */
if (!propertyContextById.contains(encodedPropertyName)) {
propertyContextById.update(encodedPropertyName, PropertyContext(propertyName, graphMLTpe))
}
val xmlEncoded = xml.Utility.escape(propertyValue.toString)
s"""$xmlEncoded"""
}
}
.mkString(lineSeparator)
}
private def xmlFormatInPlace(xmlFile: Path): Unit = {
val xml = XML.loadFile(xmlFile.toFile)
val prettyPrinter = new PrettyPrinter(120, 2)
val formatted = prettyPrinter.format(xml)
writeFile(xmlFile, formatted)
}
}