From 3c81e08fa2887add1f13dcbbfb8821145aac2e0b Mon Sep 17 00:00:00 2001 From: Amina Adewusi Date: Fri, 22 Oct 2021 20:09:48 +0100 Subject: [PATCH] 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. --- .../internal/graph/rendering/TreeView.scala | 34 +++++------ .../graph/rendering/TreeViewTest.scala | 61 +++++++++---------- project/plugins.sbt | 1 + .../sbt/internal/graph/ModuleModel.scala | 36 +++++++++++ .../internal/graph/codec/JsonProtocol.scala | 9 +++ .../graph/codec/ModuleModelFormats.scala | 29 +++++++++ protocol/src/main/contraband/treeView.contra | 9 +++ 7 files changed, 129 insertions(+), 50 deletions(-) create mode 100644 protocol/src/main/contraband-scala/sbt/internal/graph/ModuleModel.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/graph/codec/JsonProtocol.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.scala create mode 100644 protocol/src/main/contraband/treeView.contra diff --git a/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala b/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala index f3cd7b4f7..3ff701ca1 100644 --- a/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala +++ b/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala @@ -10,24 +10,20 @@ package internal package graph package rendering +import sbt.internal.graph.codec.JsonProtocol.ModuleModelFormat import sbt.io.IO +import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter } -import java.io.File -import java.io.FileOutputStream -import java.io.InputStream -import java.io.OutputStream +import java.io.{ File, FileOutputStream, InputStream, OutputStream } import java.net.URI -import scala.annotation.nowarn -import scala.annotation.tailrec -import scala.util.parsing.json.JSONArray -import scala.util.parsing.json.JSONObject +import scala.annotation.{ nowarn, tailrec } @nowarn object TreeView { def createJson(graph: ModuleGraph): String = { - val trees = graph.roots + val moduleModels = graph.roots .map(module => processSubtree(graph, module)) - .toList - JSONArray(trees).toString + val js = moduleModels.map(Converter.toJsonUnsafe(_)) + js.map(CompactPrinter).mkString("[", ",", "]") } def createLink(graphJson: String, targetDirectory: File): URI = { @@ -43,24 +39,26 @@ import scala.util.parsing.json.JSONObject graph: ModuleGraph, module: Module, parents: Set[GraphModuleId] = Set() - ): JSONObject = { + ): ModuleModel = { val cycle = parents.contains(module.id) val dependencies = if (cycle) List() else graph.dependencyMap.getOrElse(module.id, List()) val children = - dependencies.map(dependency => processSubtree(graph, dependency, parents + module.id)).toList - moduleAsJson(module, cycle, children) + dependencies + .map(dependency => processSubtree(graph, dependency, parents + module.id)) + .toVector + moduleAsModuleAgain(module, cycle, children) } - private def moduleAsJson( + private def moduleAsModuleAgain( module: Module, isCycle: Boolean, - children: List[JSONObject] - ): JSONObject = { + children: Vector[ModuleModel] + ): ModuleModel = { val eviction = module.evictedByVersion.map(version => s" (evicted by $version)").getOrElse("") val cycle = if (isCycle) " (cycle)" else "" val error = module.error.map(err => s" (errors: $err)").getOrElse("") 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 = { diff --git a/main/src/test/scala/sbt/internal/graph/rendering/TreeViewTest.scala b/main/src/test/scala/sbt/internal/graph/rendering/TreeViewTest.scala index 544629e16..6e1a96a8f 100644 --- a/main/src/test/scala/sbt/internal/graph/rendering/TreeViewTest.scala +++ b/main/src/test/scala/sbt/internal/graph/rendering/TreeViewTest.scala @@ -5,51 +5,48 @@ * Licensed under Apache License 2.0 (see LICENSE) */ -package sbt -package internal -package graph -package rendering +package sbt.internal.graph.rendering -import org.scalatest.DiagrammedAssertions -import org.scalatest.FunSuite - -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 { +import org.scalatest.{ FlatSpec, Matchers } +import sbt.internal.graph.rendering.TreeView.createJson +import sbt.internal.graph.{ GraphModuleId, Module, ModuleGraph, ModuleModel } +class TreeViewTest extends FlatSpec with Matchers { val modA = GraphModuleId("orgA", "nameA", "1.0") val modB = GraphModuleId("orgB", "nameB", "2.0") + val modC = GraphModuleId("orgC", "nameC", "3.0") val graph = ModuleGraph( - nodes = Seq(Module(modA), Module(modB)), + nodes = Seq(Module(modA), Module(modB), Module(modC)), edges = Seq( modA -> modA, modA -> modB, + modC -> modA, ) ) - test("TreeView should detect cycles and truncate") { - val json = TreeView.processSubtree(graph, Module(modA)) - val (rootText, children) = parseTree(json) - assert(rootText == modA.idString) - - val childrenText = children.map(parseTree).map(_._1) - val expected = List(s"${modA.idString} (cycle)", modB.idString) - assert(childrenText == expected) + "createJson" should "convert ModuleGraph into JSON correctly" in { + val expected = + "[{\"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\":[]}]}]}]" + Predef.assert( + createJson(graph) == expected, + s"Expected $expected, but got ${createJson(graph)}" + ) } - @nowarn("cat=unchecked") - def parseTree(json: JSONObject): (String, List[JSONObject]) = { - (json.obj.get("text"), json.obj.get("children")) match { - case (Some(text: String), Some(JSONArray(children: List[JSONObject]))) - if children.forall(_.isInstanceOf[JSONObject]) => - text -> children - case _ => - fail("a string field 'text' and an array of objects in 'children' field were expected!") - } + "processSubtree" should "detect cycles and truncate" in { + val expected = ModuleModel( + "orgC:nameC:3.0", + Vector( + ModuleModel( + "orgA:nameA:1.0", + Vector( + ModuleModel("orgA:nameA:1.0 (cycle)", Vector()), + ModuleModel("orgB:nameB:2.0", Vector()) + ) + ) + ) + ) + assert(TreeView.processSubtree(graph, Module(modC), Set()) == expected) } - } diff --git a/project/plugins.sbt b/project/plugins.sbt index 245e11f02..7f7e8fa9a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -11,3 +11,4 @@ addSbtPlugin("com.lightbend" % "sbt-whitesource" % "0.1.14") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1") addSbtPlugin("com.swoval" % "sbt-java-format" % "0.3.1") +addDependencyTreePlugin diff --git a/protocol/src/main/contraband-scala/sbt/internal/graph/ModuleModel.scala b/protocol/src/main/contraband-scala/sbt/internal/graph/ModuleModel.scala new file mode 100644 index 000000000..0d8a518a6 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/graph/ModuleModel.scala @@ -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) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/graph/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/internal/graph/codec/JsonProtocol.scala new file mode 100644 index 000000000..09155f459 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/graph/codec/JsonProtocol.scala @@ -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 \ No newline at end of file diff --git a/protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.scala new file mode 100644 index 000000000..eaf3c902c --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.scala @@ -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() + } +} +} diff --git a/protocol/src/main/contraband/treeView.contra b/protocol/src/main/contraband/treeView.contra new file mode 100644 index 000000000..11074e74b --- /dev/null +++ b/protocol/src/main/contraband/treeView.contra @@ -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] +} \ No newline at end of file