mirror of https://github.com/sbt/sbt.git
Migrates Treeview.scala to use Contraband
Migrates TreeView.scala to use Contraband from scala.util.parsing.json, because this is now deprecated. The TreeView logic is used in the dependencyBrowseTree task.
This commit is contained in:
parent
bcbef795e7
commit
3c81e08fa2
|
|
@ -10,24 +10,20 @@ package internal
|
||||||
package graph
|
package graph
|
||||||
package rendering
|
package rendering
|
||||||
|
|
||||||
|
import sbt.internal.graph.codec.JsonProtocol.ModuleModelFormat
|
||||||
import sbt.io.IO
|
import sbt.io.IO
|
||||||
|
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
|
||||||
|
|
||||||
import java.io.File
|
import java.io.{ File, FileOutputStream, InputStream, OutputStream }
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.OutputStream
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import scala.annotation.nowarn
|
import scala.annotation.{ nowarn, tailrec }
|
||||||
import scala.annotation.tailrec
|
|
||||||
import scala.util.parsing.json.JSONArray
|
|
||||||
import scala.util.parsing.json.JSONObject
|
|
||||||
|
|
||||||
@nowarn object TreeView {
|
@nowarn object TreeView {
|
||||||
def createJson(graph: ModuleGraph): String = {
|
def createJson(graph: ModuleGraph): String = {
|
||||||
val trees = graph.roots
|
val moduleModels = graph.roots
|
||||||
.map(module => processSubtree(graph, module))
|
.map(module => processSubtree(graph, module))
|
||||||
.toList
|
val js = moduleModels.map(Converter.toJsonUnsafe(_))
|
||||||
JSONArray(trees).toString
|
js.map(CompactPrinter).mkString("[", ",", "]")
|
||||||
}
|
}
|
||||||
|
|
||||||
def createLink(graphJson: String, targetDirectory: File): URI = {
|
def createLink(graphJson: String, targetDirectory: File): URI = {
|
||||||
|
|
@ -43,24 +39,26 @@ import scala.util.parsing.json.JSONObject
|
||||||
graph: ModuleGraph,
|
graph: ModuleGraph,
|
||||||
module: Module,
|
module: Module,
|
||||||
parents: Set[GraphModuleId] = Set()
|
parents: Set[GraphModuleId] = Set()
|
||||||
): JSONObject = {
|
): ModuleModel = {
|
||||||
val cycle = parents.contains(module.id)
|
val cycle = parents.contains(module.id)
|
||||||
val dependencies = if (cycle) List() else graph.dependencyMap.getOrElse(module.id, List())
|
val dependencies = if (cycle) List() else graph.dependencyMap.getOrElse(module.id, List())
|
||||||
val children =
|
val children =
|
||||||
dependencies.map(dependency => processSubtree(graph, dependency, parents + module.id)).toList
|
dependencies
|
||||||
moduleAsJson(module, cycle, children)
|
.map(dependency => processSubtree(graph, dependency, parents + module.id))
|
||||||
|
.toVector
|
||||||
|
moduleAsModuleAgain(module, cycle, children)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def moduleAsJson(
|
private def moduleAsModuleAgain(
|
||||||
module: Module,
|
module: Module,
|
||||||
isCycle: Boolean,
|
isCycle: Boolean,
|
||||||
children: List[JSONObject]
|
children: Vector[ModuleModel]
|
||||||
): JSONObject = {
|
): ModuleModel = {
|
||||||
val eviction = module.evictedByVersion.map(version => s" (evicted by $version)").getOrElse("")
|
val eviction = module.evictedByVersion.map(version => s" (evicted by $version)").getOrElse("")
|
||||||
val cycle = if (isCycle) " (cycle)" else ""
|
val cycle = if (isCycle) " (cycle)" else ""
|
||||||
val error = module.error.map(err => s" (errors: $err)").getOrElse("")
|
val error = module.error.map(err => s" (errors: $err)").getOrElse("")
|
||||||
val text = module.id.idString + eviction + error + cycle
|
val text = module.id.idString + eviction + error + cycle
|
||||||
JSONObject(Map("text" -> text, "children" -> JSONArray(children)))
|
ModuleModel(text, children)
|
||||||
}
|
}
|
||||||
|
|
||||||
def saveResource(resourcePath: String, to: File): Unit = {
|
def saveResource(resourcePath: String, to: File): Unit = {
|
||||||
|
|
|
||||||
|
|
@ -5,51 +5,48 @@
|
||||||
* Licensed under Apache License 2.0 (see LICENSE)
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package sbt
|
package sbt.internal.graph.rendering
|
||||||
package internal
|
|
||||||
package graph
|
|
||||||
package rendering
|
|
||||||
|
|
||||||
import org.scalatest.DiagrammedAssertions
|
import org.scalatest.{ FlatSpec, Matchers }
|
||||||
import org.scalatest.FunSuite
|
import sbt.internal.graph.rendering.TreeView.createJson
|
||||||
|
import sbt.internal.graph.{ GraphModuleId, Module, ModuleGraph, ModuleModel }
|
||||||
import scala.annotation.nowarn
|
|
||||||
import scala.util.parsing.json.JSONArray
|
|
||||||
import scala.util.parsing.json.JSONObject
|
|
||||||
|
|
||||||
@nowarn("msg=class JSONObject in package json is deprecated")
|
|
||||||
class TreeViewTest extends FunSuite with DiagrammedAssertions {
|
|
||||||
|
|
||||||
|
class TreeViewTest extends FlatSpec with Matchers {
|
||||||
val modA = GraphModuleId("orgA", "nameA", "1.0")
|
val modA = GraphModuleId("orgA", "nameA", "1.0")
|
||||||
val modB = GraphModuleId("orgB", "nameB", "2.0")
|
val modB = GraphModuleId("orgB", "nameB", "2.0")
|
||||||
|
val modC = GraphModuleId("orgC", "nameC", "3.0")
|
||||||
|
|
||||||
val graph = ModuleGraph(
|
val graph = ModuleGraph(
|
||||||
nodes = Seq(Module(modA), Module(modB)),
|
nodes = Seq(Module(modA), Module(modB), Module(modC)),
|
||||||
edges = Seq(
|
edges = Seq(
|
||||||
modA -> modA,
|
modA -> modA,
|
||||||
modA -> modB,
|
modA -> modB,
|
||||||
|
modC -> modA,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
test("TreeView should detect cycles and truncate") {
|
"createJson" should "convert ModuleGraph into JSON correctly" in {
|
||||||
val json = TreeView.processSubtree(graph, Module(modA))
|
val expected =
|
||||||
val (rootText, children) = parseTree(json)
|
"[{\"text\":\"orgC:nameC:3.0\",\"children\":[{\"text\":\"orgA:nameA:1.0\",\"children\":[{\"text\":\"orgA:nameA:1.0 (cycle)\",\"children\":[]},{\"text\":\"orgB:nameB:2.0\",\"children\":[]}]}]}]"
|
||||||
assert(rootText == modA.idString)
|
Predef.assert(
|
||||||
|
createJson(graph) == expected,
|
||||||
val childrenText = children.map(parseTree).map(_._1)
|
s"Expected $expected, but got ${createJson(graph)}"
|
||||||
val expected = List(s"${modA.idString} (cycle)", modB.idString)
|
)
|
||||||
assert(childrenText == expected)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@nowarn("cat=unchecked")
|
"processSubtree" should "detect cycles and truncate" in {
|
||||||
def parseTree(json: JSONObject): (String, List[JSONObject]) = {
|
val expected = ModuleModel(
|
||||||
(json.obj.get("text"), json.obj.get("children")) match {
|
"orgC:nameC:3.0",
|
||||||
case (Some(text: String), Some(JSONArray(children: List[JSONObject])))
|
Vector(
|
||||||
if children.forall(_.isInstanceOf[JSONObject]) =>
|
ModuleModel(
|
||||||
text -> children
|
"orgA:nameA:1.0",
|
||||||
case _ =>
|
Vector(
|
||||||
fail("a string field 'text' and an array of objects in 'children' field were expected!")
|
ModuleModel("orgA:nameA:1.0 (cycle)", Vector()),
|
||||||
}
|
ModuleModel("orgB:nameB:2.0", Vector())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert(TreeView.processSubtree(graph, Module(modC), Set()) == expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,4 @@ addSbtPlugin("com.lightbend" % "sbt-whitesource" % "0.1.14")
|
||||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
|
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
|
||||||
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1")
|
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1")
|
||||||
addSbtPlugin("com.swoval" % "sbt-java-format" % "0.3.1")
|
addSbtPlugin("com.swoval" % "sbt-java-format" % "0.3.1")
|
||||||
|
addDependencyTreePlugin
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
// DO NOT EDIT MANUALLY
|
||||||
|
package sbt.internal.graph
|
||||||
|
final class ModuleModel private (
|
||||||
|
val text: String,
|
||||||
|
val children: Vector[sbt.internal.graph.ModuleModel]) extends Serializable {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
|
||||||
|
case x: ModuleModel => (this.text == x.text) && (this.children == x.children)
|
||||||
|
case _ => false
|
||||||
|
})
|
||||||
|
override def hashCode: Int = {
|
||||||
|
37 * (37 * (37 * (17 + "sbt.internal.graph.ModuleModel".##) + text.##) + children.##)
|
||||||
|
}
|
||||||
|
override def toString: String = {
|
||||||
|
"ModuleModel(" + text + ", " + children + ")"
|
||||||
|
}
|
||||||
|
private[this] def copy(text: String = text, children: Vector[sbt.internal.graph.ModuleModel] = children): ModuleModel = {
|
||||||
|
new ModuleModel(text, children)
|
||||||
|
}
|
||||||
|
def withText(text: String): ModuleModel = {
|
||||||
|
copy(text = text)
|
||||||
|
}
|
||||||
|
def withChildren(children: Vector[sbt.internal.graph.ModuleModel]): ModuleModel = {
|
||||||
|
copy(children = children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object ModuleModel {
|
||||||
|
|
||||||
|
def apply(text: String, children: Vector[sbt.internal.graph.ModuleModel]): ModuleModel = new ModuleModel(text, children)
|
||||||
|
}
|
||||||
9
protocol/src/main/contraband-scala/sbt/internal/graph/codec/JsonProtocol.scala
generated
Normal file
9
protocol/src/main/contraband-scala/sbt/internal/graph/codec/JsonProtocol.scala
generated
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
// DO NOT EDIT MANUALLY
|
||||||
|
package sbt.internal.graph.codec
|
||||||
|
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
||||||
|
with sbt.internal.graph.codec.ModuleModelFormats
|
||||||
|
object JsonProtocol extends JsonProtocol
|
||||||
29
protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.scala
generated
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
// DO NOT EDIT MANUALLY
|
||||||
|
package sbt.internal.graph.codec
|
||||||
|
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||||
|
trait ModuleModelFormats { self: sbt.internal.graph.codec.ModuleModelFormats with sjsonnew.BasicJsonProtocol =>
|
||||||
|
implicit lazy val ModuleModelFormat: JsonFormat[sbt.internal.graph.ModuleModel] = new JsonFormat[sbt.internal.graph.ModuleModel] {
|
||||||
|
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.graph.ModuleModel = {
|
||||||
|
__jsOpt match {
|
||||||
|
case Some(__js) =>
|
||||||
|
unbuilder.beginObject(__js)
|
||||||
|
val text = unbuilder.readField[String]("text")
|
||||||
|
val children = unbuilder.readField[Vector[sbt.internal.graph.ModuleModel]]("children")
|
||||||
|
unbuilder.endObject()
|
||||||
|
sbt.internal.graph.ModuleModel(text, children)
|
||||||
|
case None =>
|
||||||
|
deserializationError("Expected JsObject but found None")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override def write[J](obj: sbt.internal.graph.ModuleModel, builder: Builder[J]): Unit = {
|
||||||
|
builder.beginObject()
|
||||||
|
builder.addField("text", obj.text)
|
||||||
|
builder.addField("children", obj.children)
|
||||||
|
builder.endObject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package sbt.internal.graph
|
||||||
|
@target(Scala)
|
||||||
|
@codecPackage("sbt.internal.graph.codec")
|
||||||
|
@fullCodec("JsonProtocol")
|
||||||
|
|
||||||
|
type ModuleModel {
|
||||||
|
text: String!
|
||||||
|
children: [sbt.internal.graph.ModuleModel]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue