Skip to content

Commit 6201b23

Browse files
committed
Seems to work
1 parent b5b86bd commit 6201b23

File tree

16 files changed

+96
-50
lines changed

16 files changed

+96
-50
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# CHANGELOG
2+
3+
## v0.2.34 - 2025-04-25
4+
5+
Added option to show dependency graph and repaired issue #623

build.sbt

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ lazy val scala212 = "2.12.20"
22
lazy val scala213 = "2.13.15"
33
lazy val scala3 = "3.6.3"
44
lazy val supportedScalaVersions = List(
5+
scala212,
56
scala213,
6-
scala3,
7-
scala212
7+
scala3
88
)
99

1010
val Java11 = JavaSpec.temurin("11")

examples/non_stratified.shex

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
prefix : <http://example.org/>
2+
3+
:S { :p NOT @:S }

examples/positive_recursion.shex

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
prefix : <http://example.org/>
2+
3+
# Positive recursion
4+
:S { :p @:S +; :p .* } OR [ :d ]

examples/separation.shex

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
prefix : <http://example.org/>
22

33
# Separation test
4-
:S {
5-
:p @:S +
6-
}
7-
4+
:S { :p @:S + }
85
:S1 NOT @:S

examples/separation1.shex

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
prefix : <http://example.org/>
2+
3+
# Separation test 1
4+
:S { :p @:S + }

examples/separation2.shex

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
prefix : <http://example.org/>
2+
3+
# Separation test 1
4+
:S { :p @:S + }

examples/separation4.shex

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
prefix : <http://example.org/>
2+
3+
# Separation test
4+
:S { :p @:S + }
5+
:S1 NOT @:S

modules/depGraphs/src/main/scala/es/weso/depgraphs/DepGraph.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ trait DepGraph[Node] {
4848

4949
private def haveNegativeLink(node1: Node, node2: Node): Boolean =
5050
edgeBetween(node1, node2) match {
51-
case Some(Neg) => true
52-
case Some(Pos) => false
53-
case Some(Both) => true
54-
case None => false
51+
case Some(Neg) => true
52+
case Some(Pos) => false
53+
// case Some(Both) => true
54+
case None => false
5555
}
5656

5757
def showEdges(showNode: Node => String = x => x.toString): String

modules/depGraphs/src/main/scala/es/weso/depgraphs/DepGraphJGraphT.scala

+13-8
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ case class DepGraphJGraphT[Node]() extends DepGraph[Node] with LazyLogging {
4646
// if (!graph.containsVertex(node))
4747
graph.addVertex(node)
4848

49-
private def addEdge(node1: Node, node2: Node, edge: Edge): DepGraph[Node] = {
49+
private def addEdgeInternal(node1: Node, node2: Node, edge: Edge): DepGraph[Node] = {
5050
checkVertex(node1)
5151
checkVertex(node2)
5252
graph.addEdge(node1, node2, edge)
@@ -61,14 +61,19 @@ case class DepGraphJGraphT[Node]() extends DepGraph[Node] with LazyLogging {
6161
checkVertex(node2)
6262
this.edgeBetween(node1, node2) match {
6363
case Some(pn) =>
64+
// println(s"Edge between $node1 and $node2 already exists with posNeg $pn")
6465
removeEdge(node1, node2)
65-
addEdge(node1, node2, Edge(node1, pn.combine(posNeg), node2))
66-
case None => addEdge(node1, node2, Edge(node1, posNeg, node2))
66+
val newPosNeg = pn.combine(posNeg)
67+
val result = addEdgeInternal(node1, node2, Edge(node1, newPosNeg, node2))
68+
// println(s"New edge added $node1 and $node2 posNeg $newPosNeg")
69+
result
70+
case None => addEdgeInternal(node1, node2, Edge(node1, posNeg, node2))
6771
}
6872
}
6973

7074
override def edgeBetween(node1: Node, node2: Node): Option[PosNeg] = {
71-
val outEdges = graph.edgesOf(node1).asScala.toSet
75+
val outEdges: Set[Edge] = graph.outgoingEdgesOf(node1).asScala.toSet
76+
// println(s"outEdges($node1)=$outEdges")
7277
outEdges.collect { case e: Edge if e.target == node2 => e.posNeg }.headOption
7378
}
7479

@@ -89,7 +94,7 @@ case class DepGraphJGraphT[Node]() extends DepGraph[Node] with LazyLogging {
8994
}
9095

9196
private def containsNegEdge(g: Graph[Node, Edge]): Boolean =
92-
g.edgeSet.asScala.exists(e => e.posNeg == Neg || e.posNeg == Both)
97+
g.edgeSet.asScala.exists(e => e.posNeg == Neg) // || e.posNeg == Both)
9398

9499
override def negCycles: Set[Set[(Node, Node)]] = {
95100
val scAlg: StrongConnectivityAlgorithm[Node, Edge] =
@@ -112,9 +117,9 @@ case class DepGraphJGraphT[Node]() extends DepGraph[Node] with LazyLogging {
112117

113118
def showPosNeg(pn: PosNeg): String =
114119
pn match {
115-
case Pos => "-(+)->"
116-
case Neg => "-(-)->"
117-
case Both => "-(-/+)->"
120+
case Pos => "-(+)->"
121+
case Neg => "-(-)->"
122+
// case Both => "-(-/+)->"
118123
}
119124

120125
def showEdges(showNode: Node => String): String = {

modules/depGraphs/src/main/scala/es/weso/depgraphs/PosNeg.scala

+13-11
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,29 @@ package es.weso.depgraphs
22

33
sealed trait PosNeg {
44
def change: PosNeg = this match {
5-
case Neg => Pos
6-
case Pos => Neg
7-
case Both => Both
5+
case Neg => Pos
6+
case Pos => Neg
7+
// case Both => Both
88
}
99
def combine(other: PosNeg): PosNeg
1010
}
1111

1212
case object Pos extends PosNeg {
1313
override def combine(other: PosNeg): PosNeg = other match {
14-
case Pos => Pos
15-
case Neg => Both
16-
case Both => Both
14+
case Pos => Pos
15+
case Neg => Neg
16+
// case Neg => Both
17+
// case Both => Both
1718
}
1819
}
1920
case object Neg extends PosNeg {
2021
override def combine(other: PosNeg): PosNeg = other match {
21-
case Pos => Both
22-
case Neg => Neg
23-
case Both => Both
22+
case Pos => Neg // Both
23+
case Neg => Neg
24+
// case Both => Both
2425
}
2526
}
26-
case object Both extends PosNeg {
27+
28+
/*case object Both extends PosNeg {
2729
override def combine(other: PosNeg): PosNeg = Both
28-
}
30+
}*/

modules/shex/src/main/scala/es/weso/shex/Dependencies.scala

+13-13
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,12 @@ object Dependencies {
2222
* If the set is empy, there are no negated cycles.
2323
*/
2424
def negCycles(schema: Schema): ES[Set[Set[(ShapeLabel, ShapeLabel)]]] =
25-
depGraph(schema).map(_.negCycles)
25+
schema.depGraph.map(_.negCycles)
2626

2727
def oddNegCycles(schema: Schema): ES[Set[Set[(ShapeLabel, ShapeLabel)]]] =
2828
// depGraph(schema).map(_.oddNegCycles)
2929
for {
30-
dg <- depGraph(schema)
31-
_ <- { println(s"Dependency graph: $dg"); Right(()) }
30+
dg <- schema.depGraph
3231
negCycles = dg.negCycles.filter(nc => dg.countNegLinks(nc) % 2 == 1)
3332
} yield negCycles
3433

@@ -43,15 +42,19 @@ object Dependencies {
4342
case None => emptyGraph
4443
case Some(shapes) => shapes.foldRight(emptyGraph)(addDependency(schema))
4544
}
46-
println(s"Dependency graph: $r")
45+
/*r match {
46+
case Left(s) => println(s"Error calculating dependency graph: $s")
47+
case Right(g) =>
48+
println(s"Dependency graph generated:\n${g.showEdges(_.toString)}\n---")
49+
}*/
4750
r
4851
}
4952

5053
def addDependencies(graph: DepGraph[ShapeLabel], deps: Deps): DepGraph[ShapeLabel] =
5154
deps.foldRight(graph)(combine)
5255

5356
def combine(d: Dep, g: DepGraph[ShapeLabel]): DepGraph[ShapeLabel] =
54-
// println(s"Adding edge $d to graph: $g")
57+
// println(s"Adding edge $d to graph: $g")
5558
g.addEdge(d._1, d._2, d._3)
5659

5760
def addDependency(
@@ -61,6 +64,7 @@ object Dependencies {
6164
g <- graph
6265
label <- getLabel(se)
6366
deps <- dependencies(schema, se, label, Pos)
67+
// _ <- { println(s"dependencies: ${deps}"); Right(()) }
6468
} yield addDependencies(g, deps)
6569

6670
def getLabel(se: ShapeExpr): ES[ShapeLabel] =
@@ -71,10 +75,8 @@ object Dependencies {
7175
shape: ShapeExpr,
7276
source: ShapeLabel,
7377
posNeg: PosNeg
74-
): ES[Deps] = {
75-
println(
76-
s"Calculating dependencies of shape $shape with source label $source and posNeg $posNeg"
77-
)
78+
): ES[Deps] =
79+
// println(s"Calculating dependencies of $source with shapeExpr $shape, posNeg $posNeg")
7880
shape match {
7981
case s: ShapeAnd =>
8082
s.shapeExprs.map(dependencies(schema, _, source, posNeg)).sequence[ES, Deps].map(_.flatten)
@@ -83,7 +85,7 @@ object Dependencies {
8385
s.shapeExprs.map(dependencies(schema, _, source, posNeg)).sequence[ES, Deps].map(_.flatten)
8486

8587
case s: ShapeNot =>
86-
dependencies(schema, s.shapeExpr, source, Neg)
88+
dependencies(schema, s.shapeExpr, source, posNeg.change)
8789

8890
case _: NodeConstraint => noDeps
8991

@@ -109,15 +111,13 @@ object Dependencies {
109111
case _: ShapeExternal => noDeps
110112
case sd: ShapeDecl => dependencies(schema, sd.shapeExpr, source, posNeg)
111113
}
112-
}
113114

114115
def dependenciesTripleExpr(
115116
schema: Schema,
116117
source: ShapeLabel,
117118
tripleExpr: TripleExpr,
118119
posNeg: PosNeg
119120
): ES[Deps] =
120-
// println(s"Calculating dependencies of tripleExpr $tripleExpr with source label $source and posNeg $posNeg")
121121
tripleExpr match {
122122
case t: EachOf =>
123123
// TODO: Take into account max cardinality = 0 as a negative dependency?
@@ -143,7 +143,7 @@ object Dependencies {
143143
case Some(ve) =>
144144
if (tc.max == IntMax(0)) {
145145
// TODO: Should it be negative dependency?
146-
dependencies(schema, ve, source, posNeg.change)
146+
dependencies(schema, ve, source, posNeg)
147147
} else {
148148
dependencies(schema, ve, source, posNeg)
149149
}

modules/shex/src/main/scala/es/weso/shex/ResolvedSchema.scala

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ case class ResolvedSchema(
2222
resolvedMapShapeExprs: Map[ShapeLabel, ResolvedShapeExpr],
2323
resolvedMapTripleExprs: Map[ShapeLabel, ResolvedTripleExpr],
2424
inheritanceGraph: Inheritance[ShapeLabel, ShapesRelation],
25+
// depGraph: DepGraph[ShapeLabel],
2526
labelLocationMap: Option[Map[ShapeLabel, Location]]
2627
) extends AbstractSchema {
2728

modules/shex/src/main/scala/es/weso/shex/Schema.scala

+5-6
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,13 @@ case class Schema(
101101
def addTripleExprMap(te: Map[ShapeLabel, TripleExpr]): Schema =
102102
this.copy(optTripleExprMap = Some(te))
103103

104-
def oddNegCycles: Either[String, Set[Set[(ShapeLabel, ShapeLabel)]]] =
104+
lazy val oddNegCycles: Either[String, Set[Set[(ShapeLabel, ShapeLabel)]]] =
105105
Dependencies.oddNegCycles(this)
106106

107-
def negCycles: Either[String, Set[Set[(ShapeLabel, ShapeLabel)]]] =
107+
lazy val negCycles: Either[String, Set[Set[(ShapeLabel, ShapeLabel)]]] =
108108
Dependencies.negCycles(this)
109109

110-
def depGraph: Either[String, DepGraph[ShapeLabel]] =
110+
lazy val depGraph: Either[String, DepGraph[ShapeLabel]] =
111111
Dependencies.depGraph(this)
112112

113113
def showCycles(str: Either[String, Set[Set[(ShapeLabel, ShapeLabel)]]]): String = str match {
@@ -130,16 +130,15 @@ case class Schema(
130130
_ <- shapesMap.keySet.toList.map(lbl => checkShapeLabel(lbl)).sequence
131131
} yield (())
132132

133-
private lazy val checkOddNegCycles: Either[String, Unit] = {
134-
println(s"OddNegCycles: $oddNegCycles")
133+
private lazy val checkOddNegCycles: Either[String, Unit] =
134+
// println(s"OddNegCycles: $oddNegCycles")
135135
oddNegCycles match {
136136
case Left(e) => Left(e)
137137
case Right(cs) =>
138138
if (cs.isEmpty) Right(())
139139
else
140140
Left(s"Negative cycles: ${showCycles(oddNegCycles)}")
141141
}
142-
}
143142

144143
lazy val wellFormed: Either[String, Unit] = for {
145144
_ <- checkOddNegCycles

src/main/scala/es/weso/shexs/SchemaCommand.scala

+18-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ object ShowMethod {
5454
case class SchemaCommand(
5555
schemaSpec: SchemaSpec,
5656
showInheritance: Boolean,
57+
showDependencyGraph: Boolean,
5758
showMethod: ShowMethod,
5859
showShape: Option[ShapeLabel],
5960
verbose: VerboseLevel
@@ -66,6 +67,9 @@ case class SchemaCommand(
6667
_ <-
6768
if (showInheritance) runShowInheritance(resolved)
6869
else IO.pure(())
70+
_ <-
71+
if (showDependencyGraph) showDepGraph(resolved)
72+
else IO.pure(())
6973
_ <- showShape match {
7074
case None => IO.pure(())
7175
case Some(sl) => runShowShapeLabel(sl, resolved, showMethod)
@@ -89,6 +93,16 @@ case class SchemaCommand(
8993
)
9094
}
9195

96+
private def showDepGraph(schema: ResolvedSchema): IO[Unit] = {
97+
val eitherDepGraph = schema.source.depGraph
98+
eitherDepGraph match {
99+
case Left(err) =>
100+
IO.println(s"Error obtaining dependency graph: $err")
101+
case Right(depGraph) =>
102+
IO.println(s"Dependency graph:\n${depGraph.showEdges(lbl => schema.qualify(lbl.toRDFNode))}")
103+
}
104+
}
105+
92106
private def runShowInheritance(schema: ResolvedSchema): IO[Unit] = for {
93107
inheritanceStr <-
94108
schema.inheritanceGraph.show(lbl => schema.qualify(lbl.toRDFNode), r => s" -${r.name}-> ")
@@ -130,7 +144,9 @@ object SchemaCommand {
130144

131145
val showInheritance: Opts[Boolean] =
132146
Opts.flag("show-inheritance", short = "i", help = "show inheritance graph").orFalse
133-
// val showMethod: Opts[ShowMethod] = Opts.option[String]("show-qualified", short = "q", help = "show shapes qualified by prefix declarations").orFalse
147+
148+
val showDependencyGraph: Opts[Boolean] =
149+
Opts.flag("show-dependency-graph", short = "d", help = "show dependency graph").orFalse
134150

135151
val showShape: Opts[Option[ShapeLabel]] =
136152
UriOpt.uri("shape", help = "Show shape").map(uri => IRILabel(IRI(uri))).orNone
@@ -140,6 +156,7 @@ object SchemaCommand {
140156
(
141157
SchemaSpec.schemaSpec,
142158
showInheritance,
159+
showDependencyGraph,
143160
ShowMethod.showMethodOpt,
144161
showShape,
145162
VerboseLevelOpt.verboseLevel

version.sbt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ThisBuild / version := "0.2.33"
1+
ThisBuild / version := "0.2.34"

0 commit comments

Comments
 (0)