Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fail gracefully for unsupported property types (both in schema and at runtime) #164

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions core/src/main/java/flatgraph/UnsupportedOperationException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package flatgraph;

public class UnsupportedOperationException extends RuntimeException {
public UnsupportedOperationException(String message) {
super(message);
}

public UnsupportedOperationException(String message, Throwable cause) {
super(message, cause);
}
}
20 changes: 16 additions & 4 deletions core/src/main/scala/flatgraph/DiffGraphApplier.scala
Original file line number Diff line number Diff line change
@@ -545,8 +545,14 @@ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder)
while (insertionCounter < insertions.size && insertions(insertionCounter).src.seq == insertionSeq) {
val insertion = insertions(insertionCounter)
newNeighbors(insertionBaseIndex + insertionCounter) = insertion.dst
if (newPropertyView != null && insertion.property != DefaultValue)
newPropertyView(insertionBaseIndex + insertionCounter) = insertion.property
if (newPropertyView != null && insertion.property != DefaultValue) {
try {
newPropertyView(insertionBaseIndex + insertionCounter) = insertion.property
} catch {
case _: ArrayStoreException =>
throw new UnsupportedOperationException(s"unsupported property type: ${insertion.property.getClass}")
}
}
insertionCounter += 1
}
newQty(insertionSeq + 1) = insertionBaseIndex + insertionCounter
@@ -617,8 +623,14 @@ private[flatgraph] class DiffGraphApplier(graph: Graph, diff: DiffGraphBuilder)
}

private def copyToArray[T](buf: mutable.ArrayBuffer[Any], dst: Array[T]): Unit = {
// this is a dirty hack in order to make scala type system shut up
buf.asInstanceOf[mutable.ArrayBuffer[T]].copyToArray(dst)
try {
// this is a dirty hack in order to make scala type system shut up
buf.asInstanceOf[mutable.ArrayBuffer[T]].copyToArray(dst)
} catch {
case _: ArrayStoreException =>
val typeMaybe = buf.headOption.map(property => s": ${property.getClass}").getOrElse("")
throw new UnsupportedOperationException(s"unsupported property type$typeMaybe")
}
}

private def get(a: Array[Int], idx: Int): Int = if (idx < a.length) a(idx) else a.last
2 changes: 1 addition & 1 deletion core/src/main/scala/flatgraph/Schema.scala
Original file line number Diff line number Diff line change
@@ -156,7 +156,7 @@ class FreeSchema(
case a: Array[Double] => if (a.length == 0) FormalQtyType.DoubleType else FormalQtyType.DoubleTypeWithDefault(a(0))
case a: Array[String] => if (a.length == 0) FormalQtyType.StringType else FormalQtyType.StringTypeWithDefault(a(0))
case a: Array[GNode] => FormalQtyType.RefType
case _ => ???
case other => throw new UnsupportedOperationException(s"unsupported property prototype: ${other.getClass}")
}
override def getNumberOfNodeKinds: Int = nodeLabels.length
override def getNumberOfEdgeKinds: Int = edgeLabels.length
39 changes: 39 additions & 0 deletions core/src/test/scala/flatgraph/GraphTests.scala
Original file line number Diff line number Diff line change
@@ -796,6 +796,45 @@ class GraphTests extends AnyWordSpec with Matchers {
Accessors.getNodePropertyOptionCompat(v0, 2) shouldBe Some(Seq("c", "d"))
}

"report error when trying to use unsupported property types" in {
class A(wrapped: String) // we don't support arbitrary classes as property types

// ensure that we throw an exception when trying to define a schema with an arbitrary class as a node property type
intercept[UnsupportedOperationException] {
TestSchema.make(1, 1, nodePropertyPrototypes = Array(Array.empty[A]))
}.getMessage should include("unsupported property prototype")

// same for edge properties
intercept[UnsupportedOperationException] {
TestSchema.make(1, 1, edgePropertyPrototypes = Array(Array.empty[A]))
}.getMessage should include("unsupported property prototype")

// now let's define a valid schema and try to update a node/edge property to something illegal via a DiffGraph
val schema = TestSchema.make(
nodeKinds = 1,
edgeKinds = 1,
properties = 1,
edgePropertyPrototypes = Array(Array.empty[String]),
nodePropertyPrototypes = Array(Array.empty[String]),
formalQtys = Array(FormalQtyType.QtyOption, null, FormalQtyType.QtyMulti, null)
)

val g = new Graph(schema)
val v0 = new GenericDNode(0)
val v1 = new GenericDNode(0)
DiffGraphApplier.applyDiff(g, new DiffGraphBuilder(schema).addNode(v0).addNode(v1))

// try to update a node property to something unsupported via a DiffGraph
intercept[UnsupportedOperationException] {
DiffGraphApplier.applyDiff(g, new DiffGraphBuilder(schema)._setNodeProperty(v0.storedRef.get, 0, new A("should not work")))
}.getMessage should include("unsupported property type")

// same for edge properties
intercept[UnsupportedOperationException] {
DiffGraphApplier.applyDiff(g, new DiffGraphBuilder(schema)._addEdge(v0, v1, 0, new A("should not work")))
}.getMessage should include("unsupported property type")
}

"Support custom domain classes for detached nodes" in {
class CustomNode extends DNode {
override type StoredNodeType = GNode