Skip to content

Commit 5042694

Browse files
authored
use scala primitives (e.g. Integer -> Int) (#271)
* use scala primitives (e.g. Integer -> Int) motivation: minify diff for flatgraph migration * fix autoconversion bug attention with autoconversions around `Option[Int]` and similar: ``` scala> Option(null: Integer) val res0: Option[Integer] = None scala> val o: Option[Int] = Option(null: Integer) val o: Option[Int] = Some(0) scala> val o: Option[Int] = Option(null: Integer).asInstanceOf[Option[Int]] val o: Option[Int] = None ``` * Properties.java -> Properties.scala to avoid java interop issues scala performs quite some magic to handle Option[Int], and the bytecode looks differently if javac or scalac compile something like `val x: Option[Int]`: ``` // scalac: public static flatgraph.SinglePropertyKey<java.lang.Object> ArgumentIndex(); //javac: public static final overflowdb.PropertyKey<scala.Int> ARGUMENT_INDEX; ``` Hence, the best way forward is to define these constants in scala.
1 parent 5c45082 commit 5042694

File tree

6 files changed

+84
-76
lines changed

6 files changed

+84
-76
lines changed

codegen/src/main/scala/overflowdb/codegen/CodeGen.scala

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -254,20 +254,26 @@ class CodeGen(schema: Schema) {
254254
})
255255
}
256256

257-
writeConstantsFile("Properties", schema.properties.map { property =>
258-
val src = {
257+
// Properties.scala
258+
val propertyKeysConstantsSource = {
259+
schema.properties.map { property =>
259260
val valueType = typeFor(property)
260-
val cardinality = property.cardinality
261-
import Property.Cardinality
262-
val completeType = cardinality match {
263-
case Cardinality.One(_) => valueType
264-
case Cardinality.ZeroOrOne => valueType
265-
case Cardinality.List => s"scala.collection.IndexedSeq<$valueType>"
261+
val completeType = property.cardinality match {
262+
case Property.Cardinality.One(_) => valueType
263+
case Property.Cardinality.ZeroOrOne => valueType
264+
case Property.Cardinality.List => s"IndexedSeq[$valueType]"
266265
}
267-
s"""public static final overflowdb.PropertyKey<$completeType> ${property.name} = new overflowdb.PropertyKey<>("${property.name}");"""
266+
s"""val ${camelCaseCaps(property.name)}: overflowdb.PropertyKey[$completeType] = new overflowdb.PropertyKey("${property.name}")""".stripMargin.trim
268267
}
269-
ConstantContext(property.name, src, property.comment)
270-
})
268+
}.mkString("\n\n")
269+
val file = baseDir.createChild("Properties.scala").write(
270+
s"""package ${schema.basePackage}
271+
|
272+
|object Properties {
273+
|$propertyKeysConstantsSource
274+
|}""".stripMargin
275+
)
276+
results.append(file)
271277

272278
results.toSeq
273279
}
@@ -343,7 +349,7 @@ class CodeGen(schema: Schema) {
343349
properties.map { property =>
344350
val name = property.name
345351
val nameCamelCase = camelCase(name)
346-
val tpe = getCompleteType(property)
352+
val tpe = getCompleteType(property, nullable = false)
347353

348354
property.cardinality match {
349355
case Cardinality.One(_) =>
@@ -479,7 +485,7 @@ class CodeGen(schema: Schema) {
479485
def generateNodeBaseTypeSource(nodeBaseType: NodeBaseType): String = {
480486
val className = nodeBaseType.className
481487
val properties = nodeBaseType.properties
482-
val propertyAccessors = Helpers.propertyAccessors(properties)
488+
val propertyAccessors = Helpers.propertyAccessors(properties, nullable = false)
483489

484490
val mixinsForBaseTypes = nodeBaseType.extendz.map { baseTrait =>
485491
s"with ${baseTrait.className}"
@@ -601,7 +607,7 @@ class CodeGen(schema: Schema) {
601607

602608
val newNodePropertySetters = nodeBaseType.properties.map { property =>
603609
val camelCaseName = camelCase(property.name)
604-
val tpe = getCompleteType(property)
610+
val tpe = getCompleteType(property, nullable = false)
605611
s"def ${camelCaseName}_=(value: $tpe): Unit"
606612
}.mkString(lineSeparator)
607613

@@ -856,7 +862,7 @@ class CodeGen(schema: Schema) {
856862
case Cardinality.One(_) =>
857863
s"""this._$memberName = $newNodeCasted.$memberName""".stripMargin
858864
case Cardinality.ZeroOrOne =>
859-
s"""this._$memberName = $newNodeCasted.$memberName.orNull""".stripMargin
865+
s"""this._$memberName = $newNodeCasted.$memberName match { case None => null; case Some(value) => value }""".stripMargin
860866
case Cardinality.List =>
861867
s"""this._$memberName = if ($newNodeCasted.$memberName != null) $newNodeCasted.$memberName else collection.immutable.ArraySeq.empty""".stripMargin
862868
}
@@ -993,7 +999,7 @@ class CodeGen(schema: Schema) {
993999
s"""trait ${className}Base extends AbstractNode $mixinsForExtendedNodesBase $mixinsForMarkerTraits {
9941000
| def asStored : StoredNode = this.asInstanceOf[StoredNode]
9951001
|
996-
| ${Helpers.propertyAccessors(properties)}
1002+
| ${Helpers.propertyAccessors(properties, nullable = false)}
9971003
|
9981004
| $abstractContainedNodeAccessors
9991005
|}
@@ -1021,7 +1027,7 @@ class CodeGen(schema: Schema) {
10211027
val nodeRefImpl = {
10221028
val propertyDelegators = properties.map { key =>
10231029
val name = camelCase(key.name)
1024-
s"""override def $name: ${getCompleteType(key)} = get().$name"""
1030+
s"""override def $name: ${getCompleteType(key, nullable = false)} = get().$name"""
10251031
}.mkString(lineSeparator)
10261032

10271033
val propertyDefaultValues = propertyDefaultValueImpl(s"$className.PropertyDefaults", properties)
@@ -1095,14 +1101,14 @@ class CodeGen(schema: Schema) {
10951101

10961102
val updateSpecificPropertyImpl: String = {
10971103
import Property.Cardinality
1098-
def caseEntry(name: String, accessorName: String, cardinality: Cardinality, baseType: String) = {
1104+
def caseEntry(name: String, accessorName: String, cardinality: Cardinality, baseType: String, baseTypeNullable: String) = {
10991105
val setter = cardinality match {
11001106
case Cardinality.One(_) | Cardinality.ZeroOrOne =>
11011107
s"value.asInstanceOf[$baseType]"
11021108
case Cardinality.List =>
11031109
s"""value match {
11041110
| case null => collection.immutable.ArraySeq.empty
1105-
| case singleValue: $baseType => collection.immutable.ArraySeq(singleValue)
1111+
| case singleValue: $baseTypeNullable => collection.immutable.ArraySeq(singleValue)
11061112
| case coll: IterableOnce[Any] if coll.iterator.isEmpty => collection.immutable.ArraySeq.empty
11071113
| case arr: Array[_] if arr.isEmpty => collection.immutable.ArraySeq.empty
11081114
| case arr: Array[_] => collection.immutable.ArraySeq.unsafeWrapArray(arr).asInstanceOf[IndexedSeq[$baseType]]
@@ -1120,10 +1126,10 @@ class CodeGen(schema: Schema) {
11201126
s"""|case "$name" => this._$accessorName = $setter"""
11211127
}
11221128

1123-
val forKeys = properties.map(p => caseEntry(p.name, camelCase(p.name), p.cardinality, typeFor(p))).mkString(lineSeparator)
1129+
val forKeys = properties.map(p => caseEntry(p.name, camelCase(p.name), p.cardinality, typeFor(p), typeFor(p, nullable = true))).mkString(lineSeparator)
11241130

11251131
val forContainedNodes = nodeType.containedNodes.map(containedNode =>
1126-
caseEntry(containedNode.localName, containedNode.localName, containedNode.cardinality, containedNode.classNameForStoredNode)
1132+
caseEntry(containedNode.localName, containedNode.localName, containedNode.cardinality, containedNode.classNameForStoredNode, containedNode.classNameForStoredNode)
11271133
).mkString(lineSeparator)
11281134

11291135
s"""override protected def updateSpecificProperty(key:String, value: Object): Unit = {
@@ -1161,10 +1167,11 @@ class CodeGen(schema: Schema) {
11611167
val fieldName = s"_$publicName"
11621168
val (publicType, tpeForField, fieldAccessor, defaultValue) = {
11631169
val valueType = typeFor(property)
1170+
val valueTypeNullable = typeFor(property, nullable = true)
11641171
property.cardinality match {
11651172
case Cardinality.One(_) =>
1166-
(valueType, valueType, fieldName, s"$className.PropertyDefaults.${property.className}")
1167-
case Cardinality.ZeroOrOne => (s"Option[$valueType]", valueType, s"Option($fieldName)", "null")
1173+
(valueType, valueTypeNullable, fieldName, s"$className.PropertyDefaults.${property.className}")
1174+
case Cardinality.ZeroOrOne => (s"Option[$valueType]", valueTypeNullable, s"Option($fieldName).asInstanceOf[Option[$valueType]]", "null")
11681175
case Cardinality.List => (s"IndexedSeq[$valueType]", s"IndexedSeq[$valueType]", fieldName, "collection.immutable.ArraySeq.empty")
11691176
}
11701177
}
@@ -1732,27 +1739,29 @@ class CodeGen(schema: Schema) {
17321739

17331740
def generateNewNodeSource(nodeType: NodeType, properties: Seq[Property[?]], inEdges: Map[String, Set[String]], outEdges: Map[String, Set[String]]) = {
17341741
import Property.Cardinality
1735-
case class FieldDescription(name: String, valueType: String, fullType: String, cardinality: Cardinality)
1742+
case class FieldDescription(name: String, valueType: String, valueTypeNullable: String, fullType: String, cardinality: Cardinality)
17361743
val fieldDescriptions = mutable.ArrayBuffer.empty[FieldDescription]
17371744

17381745
for (property <- properties) {
17391746
fieldDescriptions += FieldDescription(
17401747
camelCase(property.name),
17411748
typeFor(property),
1742-
getCompleteType(property),
1749+
typeFor(property, nullable = true),
1750+
getCompleteType(property, nullable = false),
17431751
property.cardinality)
17441752
}
17451753

17461754
for (containedNode <- nodeType.containedNodes) {
17471755
fieldDescriptions += FieldDescription(
17481756
containedNode.localName,
17491757
typeFor(containedNode),
1758+
typeFor(containedNode),
17501759
getCompleteType(containedNode),
17511760
containedNode.cardinality)
17521761
}
17531762

17541763
val memberVariables = fieldDescriptions.reverse.map {
1755-
case FieldDescription(name, _, fullType, cardinality) =>
1764+
case FieldDescription(name, _, _, fullType, cardinality) =>
17561765
val defaultValue = cardinality match {
17571766
case Cardinality.One(default) => defaultValueImpl(default)
17581767
case Cardinality.ZeroOrOne => "None"
@@ -1812,22 +1821,22 @@ class CodeGen(schema: Schema) {
18121821
val mixins = nodeType.extendz.map{baseType => s"with ${baseType.className}New"}.mkString(" ")
18131822

18141823
val propertySettersImpl = fieldDescriptions.map {
1815-
case FieldDescription(name, valueType , _, cardinality) =>
1824+
case FieldDescription(name, valueType, valueTypeNullable, _, cardinality) =>
18161825
cardinality match {
18171826
case Cardinality.One(_) =>
1818-
s"""def $name(value: $valueType): this.type = {
1827+
s"""def $name(value: $valueTypeNullable): this.type = {
18191828
| this.$name = value
18201829
| this
18211830
|}
18221831
|""".stripMargin
18231832

18241833
case Cardinality.ZeroOrOne =>
1825-
s"""def $name(value: $valueType): this.type = {
1826-
| this.$name = Option(value)
1834+
s"""def $name(value: $valueTypeNullable): this.type = {
1835+
| this.$name = Option(value).asInstanceOf[Option[$valueType]]
18271836
| this
18281837
|}
18291838
|
1830-
|def $name(value: Option[$valueType]): this.type = $name(value.orNull)
1839+
|def $name(value: Option[$valueType]): this.type = $name(value match { case None => null; case Some(value) => value: $valueTypeNullable })
18311840
|""".stripMargin
18321841

18331842
case Cardinality.List =>

codegen/src/main/scala/overflowdb/codegen/Helpers.scala

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,17 @@ object Helpers {
3232
case nonEmptyString => Some(nonEmptyString)
3333
}
3434

35-
def typeFor[A](property: Property[A]): String = {
36-
val isMandatory = property.isMandatory
35+
def typeFor[A](property: Property[A], nullable: Boolean = false): String = {
3736
property.valueType match {
38-
case ValueType.Boolean => if (isMandatory) "Boolean" else "java.lang.Boolean"
37+
case ValueType.Boolean => if (nullable) "java.lang.Boolean" else "Boolean"
3938
case ValueType.String => "String"
40-
case ValueType.Byte => if (isMandatory) "Byte" else "java.lang.Byte"
41-
case ValueType.Short => if (isMandatory) "Short" else "java.lang.Short"
42-
case ValueType.Int => if (isMandatory) "scala.Int" else "Integer"
43-
case ValueType.Long => if (isMandatory) "Long" else "java.lang.Long"
44-
case ValueType.Float => if (isMandatory) "Float" else "java.lang.Float"
45-
case ValueType.Double => if (isMandatory) "Double" else "java.lang.Double"
46-
case ValueType.Char => if (isMandatory) "scala.Char" else "Character"
39+
case ValueType.Byte => if (nullable) "java.lang.Byte" else "Byte"
40+
case ValueType.Short => if (nullable) "java.lang.Short" else "Short"
41+
case ValueType.Int => if (nullable) "Integer" else "scala.Int"
42+
case ValueType.Long => if (nullable) "java.lang.Long" else "Long"
43+
case ValueType.Float => if (nullable) "java.lang.Float" else "Float"
44+
case ValueType.Double => if (nullable) "java.lang.Double" else "Double"
45+
case ValueType.Char => if (nullable) "Character" else "scala.Char"
4746
case ValueType.List => "Seq[_]"
4847
case ValueType.NodeRef => "overflowdb.NodeRef[_]"
4948
case ValueType.Unknown => "java.lang.Object"
@@ -120,8 +119,8 @@ object Helpers {
120119
}
121120
}
122121

123-
def getCompleteType[A](property: Property[?]): String =
124-
getCompleteType(property.cardinality, typeFor(property))
122+
def getCompleteType[A](property: Property[?], nullable: Boolean = true): String =
123+
getCompleteType(property.cardinality, typeFor(property, nullable))
125124

126125
def typeFor(containedNode: ContainedNode): String = {
127126
containedNode.nodeType match {
@@ -200,10 +199,10 @@ object Helpers {
200199
}.mkString(s"$lineSeparator| ")
201200
}
202201

203-
def propertyAccessors(properties: Seq[Property[?]]): String = {
202+
def propertyAccessors(properties: Seq[Property[?]], nullable: Boolean = true): String = {
204203
properties.map { property =>
205204
val camelCaseName = camelCase(property.name)
206-
val tpe = getCompleteType(property)
205+
val tpe = getCompleteType(property, nullable = nullable)
207206
s"def $camelCaseName: $tpe"
208207
}.mkString(lineSeparator)
209208
}

integration-tests/tests/src/test/scala/Schema01Test.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import testschema01.nodes._
66
import testschema01.edges._
77
import testschema01.traversal._
88
import scala.jdk.CollectionConverters.IteratorHasAsScala
9+
910
class Schema01Test extends AnyWordSpec with Matchers {
1011
import testschema01.traversal._
1112
"constants" in {
1213
PropertyNames.NAME shouldBe "NAME"
13-
Properties.ORDER.name shouldBe "ORDER"
14+
Properties.Order.name shouldBe "ORDER"
1415
PropertyNames.ALL.contains("OPTIONS") shouldBe true
15-
Properties.ALL.contains(Properties.OPTIONS) shouldBe true
1616

1717
NodeTypes.NODE1 shouldBe "NODE1"
1818
NodeTypes.ALL.contains(Node2.Label) shouldBe true
@@ -42,8 +42,8 @@ class Schema01Test extends AnyWordSpec with Matchers {
4242

4343
"lookup and traverse nodes/edges/properties" in {
4444
// generic traversal
45-
graph.nodes.asScala.property(Properties.NAME).toSetMutable shouldBe Set("node 1a", "node 1b", "node 2a", "node 2b")
46-
graph.edges.asScala.property(Properties.NAME).toSetMutable shouldBe Set("edge 2")
45+
graph.nodes.asScala.property(Properties.Name).toSetMutable shouldBe Set("node 1a", "node 1b", "node 2a", "node 2b")
46+
graph.edges.asScala.property(Properties.Name).toSetMutable shouldBe Set("edge 2")
4747
node1Traversal.out.toList shouldBe Seq(node2a)
4848
node1Traversal.name.toSetMutable shouldBe Set("node 1a", "node 1b")
4949
node1Traversal.order.l shouldBe Seq(2)
@@ -58,7 +58,7 @@ class Schema01Test extends AnyWordSpec with Matchers {
5858
val edge2Specific = edge2.asInstanceOf[Edge2]
5959
val name: String = node1aSpecific.name
6060
name shouldBe "node 1a"
61-
val o1: Option[Integer] = node1aSpecific.order
61+
val o1: Option[Int] = node1aSpecific.order
6262
node1aSpecific.order shouldBe Some(2)
6363
node1bSpecific.order shouldBe None
6464
val o2: Seq[String] = node2aSpecific.options
@@ -97,7 +97,7 @@ class Schema01Test extends AnyWordSpec with Matchers {
9797
.name("name1")
9898
.node3(node3)
9999
.options(Seq("one", "two", "three"))
100-
.placements(Seq(1,2,3): Seq[Integer])
100+
.placements(Seq(1,2,3))
101101

102102
val builder = new BatchedUpdate.DiffGraphBuilder
103103
builder.addNode(newNode2)

integration-tests/tests/src/test/scala/Schema02Test.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class Schema02Test extends AnyWordSpec with Matchers {
5555
copy.order(3)
5656
copy.order shouldBe Some(3)
5757

58-
copy.order(Some(4: Integer))
58+
copy.order(Some(4))
5959
copy.order shouldBe Some(4)
6060

6161
original.name shouldBe "A"

integration-tests/tests/src/test/scala/Schema04Test.scala

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,18 @@ class Schema04Test extends AnyWordSpec with Matchers {
7878
val node2 = graph.addNode(Node1.Label).asInstanceOf[Node1]
7979
val edge1 = node1.addEdge(Edge1.Label, node2).asInstanceOf[Edge1]
8080
val properties = Seq(
81-
Properties.BOOL.of(false),
82-
Properties.STR.of("foo"),
83-
Properties.BYTE.of(100: Byte),
84-
Properties.SHORT.of(101: Short),
85-
Properties.INT.of(102),
86-
Properties.LONG.of(103),
87-
Properties.FLOAT1.of(Float.NaN),
88-
Properties.FLOAT2.of(104.4f),
89-
Properties.DOUBLE1.of(Double.NaN),
90-
Properties.DOUBLE2.of(105.5),
91-
Properties.CHAR.of('Z'),
92-
Properties.INT_LIST.of(ArraySeq(3, 4, 5)),
81+
Properties.Bool.of(false),
82+
Properties.Str.of("foo"),
83+
Properties.Byte.of(100: Byte),
84+
Properties.Short.of(101: Short),
85+
Properties.Int.of(102),
86+
Properties.Long.of(103),
87+
Properties.Float1.of(Float.NaN),
88+
Properties.Float2.of(104.4f),
89+
Properties.Double1.of(Double.NaN),
90+
Properties.Double2.of(105.5),
91+
Properties.Char.of('Z'),
92+
Properties.IntList.of(ArraySeq(3, 4, 5)),
9393
)
9494
properties.foreach(node1.setProperty)
9595
properties.foreach(edge1.setProperty)
@@ -164,8 +164,8 @@ class Schema04Test extends AnyWordSpec with Matchers {
164164
val node1 = graph.addNode(Node1.Label).asInstanceOf[Node1]
165165
val node2 = graph.addNode(Node1.Label).asInstanceOf[Node1]
166166
val edge1 = node1.addEdge(Edge1.Label, node2).asInstanceOf[Edge1]
167-
node1.setProperty(Properties.INT_LIST.name, Array(1,2,3))
168-
edge1.setProperty(Properties.INT_LIST.name, Array(3,4,5))
167+
node1.setProperty(Properties.IntList.name, Array(1,2,3))
168+
edge1.setProperty(Properties.IntList.name, Array(3,4,5))
169169

170170
node1.intList shouldBe IndexedSeq(1,2,3)
171171
edge1.intList shouldBe IndexedSeq(3,4,5)
@@ -175,7 +175,7 @@ class Schema04Test extends AnyWordSpec with Matchers {
175175
val node1 = NewNode1()
176176
.bool(true)
177177
.str("foo")
178-
.intList(Seq(1,2,3): Seq[Integer])
178+
.intList(Seq(1,2,3))
179179

180180
val node2 = NewNode1().node1Inner(node1)
181181

integration-tests/tests/src/test/scala/Schema05Test.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ class Schema05Test extends AnyWordSpec with Matchers {
4949
val node2 = graph.addNode(Node1.Label).asInstanceOf[Node1]
5050
val edge1 = node1.addEdge(Edge1.Label, node2).asInstanceOf[Edge1]
5151
val properties = Seq(
52-
Properties.BOOL.of(false),
53-
Properties.STR.of("foo"),
54-
Properties.BYTE.of(100: Byte),
55-
Properties.SHORT.of(101: Short),
56-
Properties.INT.of(102),
57-
Properties.LONG.of(103),
58-
Properties.FLOAT.of(104.4f),
59-
Properties.DOUBLE.of(105.5),
60-
Properties.CHAR.of('Z'),
52+
Properties.Bool.of(false),
53+
Properties.Str.of("foo"),
54+
Properties.Byte.of(100: Byte),
55+
Properties.Short.of(101: Short),
56+
Properties.Int.of(102),
57+
Properties.Long.of(103),
58+
Properties.Float.of(104.4f),
59+
Properties.Double.of(105.5),
60+
Properties.Char.of('Z'),
6161
)
6262
properties.foreach(node1.setProperty)
6363
properties.foreach(edge1.setProperty)

0 commit comments

Comments
 (0)