// ĠCopyright Ġ 2 0 0 0 - 2 0 2 1 ĠJetBrains Ġs . r . o . Ġand Ġcontributors . ĠUse Ġof Ġthis Ġsource Ġcode Ġis Ġgoverned Ġby Ġthe ĠApache Ġ 2 . 0 Ġlicense Ġthat Ġcan Ġbe Ġfound Ġin Ġthe ĠLICENSE Ġfile . Ċ package Ġcom . intellij . ide . plugins Ċ Ċ import Ġcom . fasterxml . jackson . core . Json Factory Ċ import Ġcom . fasterxml . jackson . core . Json Generator Ċ import Ġcom . intellij . util . io . Base 6 2 Ċ import Ġcom . intellij . util . io . Digest Util Ċ import Ġcom . intellij . util . io . jackson . Int elli J PrettyPrinter Ċ import Ġcom . intellij . util . io . jackson . array Ċ import Ġcom . intellij . util . io . jackson . obj Ċ import Ġit . un imi . ds i . fast util . bytes . Byte Arrays Ċ import Ġit . un imi . ds i . fast util . objects . Object 2 Int Open Custom HashMap Ċ import Ġjava . io . File Ċ import Ġjava . io . String Writer Ċ import Ġjava . io . Writer Ċ import Ġjava . nio . file . Files Ċ import Ġjava . nio . file . Path Ċ import Ġjava . util . function . Bi Consumer Ċ Ċ private Ġclass ĠId Generator Ġ{ ĊĠ Ġprivate Ġval Ġcollisions Ġ= ĠObject 2 Int Open Custom HashMap ( Byte Arrays . HASH _ STR ATE GY ) ĊĊĠ Ġfun ĠgetId ( long Id : ĠString ): ĠString Ġ{ ĊĠĠĠ Ġval Ġshort Id Ġ= ĠBase 6 2 . encode ( Digest Util . sha 2 5 6 (). digest ( long Id . toByteArray ())). copyOf Range ( 0 , Ġ 2 ) ĊĠĠĠ Ġreturn Ġ"${ short Id [ 0 ]. to Char ()} ${ short Id [ 1 ]. to Char ()} ${ coll isions . addTo ( short Id , Ġ 1 ). take If Ġ{ Ġit Ġ!= Ġ 0 Ġ} Ġ?: Ġ"" }" ĊĠ Ġ} Ċ } Ċ Ċ internal Ġclass ĠPlugin Graph Writer ( private Ġval Ġplugin Id To Info : ĠMap < String , ĠModule Info >) Ġ{ ĊĠ Ġprivate Ġval Ġnode Info To Id Ġ= ĠLinkedHashMap < Module Info , ĠString >() ĊĊĠ Ġprivate Ġval Ġdependency Links Ġ= ĠLinkedHashMap < String , ĠMutable List < String >>() ĊĠ Ġprivate Ġval Ġcontent Links Ġ= ĠLinkedHashMap < String , ĠMutable List < String >>() ĊĠ Ġprivate Ġval Ġid Generator Ġ= ĠId Generator () ĊĊĠ Ġfun Ġwrite ( out File : ĠPath ) Ġ{ ĊĠĠĠ Ġval Ġstring Writer Ġ= ĠString Writer () ĊĠĠĠ ĠwriteTo ( string Writer ) ĊĠĠĠ ĠFiles . writeString ( out File , Ġstring Writer . buffer ) ĊĠ Ġ} ĊĊĠ Ġfun ĠwriteTo ( out : ĠWriter , Ġpretty Print : ĠBoolean Ġ= Ġtrue ) Ġ{ ĊĠĠĠ Ġval Ġwriter Ġ= ĠJson Factory (). create Generator ( out ) ĊĠĠĠ Ġif Ġ( pretty Print ) Ġ{ ĊĠĠĠĠĠ Ġwriter . pretty Printer Ġ= ĠIntelliJ PrettyPrinter () ĊĠĠĠ Ġ} ĊĠĠĠ Ġwriter . use Ġ{ ĊĠĠĠĠĠ Ġwrite Graph ( writer ) ĊĠĠĠ Ġ} ĊĠ Ġ} ĊĊĠ Ġprivate Ġfun Ġwrite Graph ( writer : ĠJson Generator ) Ġ{ ĊĠĠĠ Ġwriter . array Ġ{ ĊĠĠĠĠĠ Ġval Ġentries Ġ= Ġplugin Id To Info . entries . to Mutable List () ĊĠĠĠĠĠ Ġentries . sortBy Ġ{ Ġit . value . source ModuleName Ġ} ĊĠĠĠĠĠ Ġfor Ġ( entry Ġin Ġentries ) Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġitem Ġ= Ġentry . value ĊĠĠĠĠĠĠĠ Ġif Ġ( item . packageName Ġ== Ġnull Ġ&& Ġ! has Content Or Dependencies In V 2 Format ( item . descriptor )) Ġ{ ĊĠĠĠĠĠĠĠĠĠ Ġcontinue ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġwrite Module Info ( writer Ġ= Ġwriter , Ġitem Ġ= Ġitem , ĠparentId Ġ= Ġnull ) ĊĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠ Ġ// Ġinfer Ġnode Ġfrom Ġdependencies Ġ- Ġafter Ġwriting Ġcontent Ġto Ġensure Ġthat Ġnode Ġis Ġdescribed Ġif Ġit Ġis Ġnot Ġin Ġcontent , ĊĠĠĠĠĠ Ġ// Ġfake Ġmodules Ġ( com . intellij . modules .* ) ĊĠĠĠĠĠ Ġfor Ġ( entry Ġin Ġjava . util . List . copyOf ( node Info To Id . entries )) Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ(! entry . key . dependencies . isEmpty ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠ Ġwrite Dependencies ( entry . key , Ġwriter , Ġentry . value ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠ Ġwrite Links ( writer , Ġcontent Links , Ġis Content Ġ= Ġtrue ) ĊĠĠĠĠĠ Ġwrite Links ( writer , Ġdependency Links , Ġis Content Ġ= Ġfalse ) ĊĠĠĠ Ġ} ĊĠ Ġ} ĊĊĠ Ġprivate Ġfun Ġwrite Module Info ( writer : ĠJson Generator , Ġitem : ĠModule Info , ĠparentId : ĠString ?) Ġ{ ĊĠĠĠ Ġassert (! node Info To Id . containsKey ( item )) ĊĊĠĠĠ Ġif Ġ( is Node Skipped ( item )) Ġ{ ĊĠĠĠĠĠ Ġreturn ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġval ĠnodeName Ġ= Ġitem . name Ġ?: Ġitem . source ModuleName ĊĠĠĠ Ġval Ġid Ġ= Ġid Generator . getId ( nodeName ) ĊĠĠĠ Ġvar Ġcompound Id : ĠString ? Ġ= Ġnull ĊĠĠĠ Ġif Ġ(! item . content . isEmpty ()) Ġ{ ĊĠĠĠĠĠ Ġcompound Id Ġ= Ġ" c $ id " ĊĠĠĠĠĠ Ġwriter . obj Ġ{ ĊĠĠĠĠĠĠĠ Ġwriter . writeString Field (" group ", Ġ" nodes ") ĊĠĠĠĠĠĠĠ Ġwriter . obj (" data ") Ġ{ ĊĠĠĠĠĠĠĠĠĠ Ġwriter . writeString Field (" id ", Ġcompound Id ) ĊĠĠĠĠĠĠĠĠĠ Ġwriter . writeString Field (" n ", Ġitem . plugin Id !! ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġnode Info To Id . put ( item , Ġid ) ĊĠĠĠ Ġwriter . obj Ġ{ ĊĠĠĠĠĠ Ġ// Ġfor Ġus Ġis Ġvery Ġimportant Ġto Ġunderstand Ġdependencies Ġbetween Ġsource Ġmodules , Ġthat 's Ġwhy Ġon Ġgraph Ġsource Ġmodule Ġname Ġis Ġused ĊĠĠĠĠĠ Ġ// Ġfor Ġplugins Ġas Ġnode Ġname ĊĠĠĠĠĠ Ġwriter . writeString Field (" group ", Ġ" nodes ") ĊĠĠĠĠĠ Ġwriter . obj (" data ") Ġ{ ĊĠĠĠĠĠĠĠ Ġwriter . writeString Field (" id ", Ġid ) ĊĠĠĠĠĠĠĠ Ġwriter . writeString Field (" name ", ĠnodeName ) ĊĠĠĠĠĠĠĠ Ġwriter . writeString Field (" n ", Ġget ShortName ( nodeName )) ĊĠĠĠĠĠĠĠ Ġwriter . writeString Field (" package ", Ġitem . packageName ) ĊĠĠĠĠĠĠĠ Ġwriter . writeString Field (" source Module ", Ġitem . source ModuleName ) ĊĠĠĠĠĠĠĠ Ġwriter . writeString Field (" descriptor ", Ġpath To Short String ( item . descriptor File ). replace ( File . separator Char , Ġ'/ ')) ĊĠĠĠĠĠĠĠ Ġitem . plugin Id ?. let Ġ{ ĊĠĠĠĠĠĠĠĠĠ Ġwriter . writeString Field (" plugin Id ", Ġit ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ( compound Id Ġ?: ĠparentId )?. let Ġ{ ĊĠĠĠĠĠĠĠĠĠ Ġwriter . writeString Field (" parent ", Ġit ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġwriter . write Number Field (" type ", ĠgetItem NodeType ( item )) ĊĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġif Ġ(! item . content . isEmpty ()) Ġ{ ĊĠĠĠĠĠ Ġfor Ġ( child Ġin Ġitem . content ) Ġ{ ĊĠĠĠĠĠĠĠ Ġwrite Module Info ( writer Ġ= Ġwriter , Ġitem Ġ= Ġchild , ĠparentId Ġ= Ġcompound Id ) ĊĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠ Ġfor Ġ( child Ġin Ġitem . content ) Ġ{ ĊĠĠĠĠĠĠĠ Ġcontent Links . compute If Absent ( id ) Ġ{ Ġmutable ListOf () Ġ}. add ( node Info To Id . get ( child ) !! ) ĊĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĠ Ġ} ĊĊĠ Ġprivate Ġfun Ġwrite Dependencies ( dependent Info : ĠModule Info , Ġwriter : ĠJson Generator , Ġdependent Id : ĠString ) Ġ{ ĊĠĠĠ Ġfor Ġ( ref Ġin Ġdependent Info . dependencies ) Ġ{ ĊĠĠĠĠĠ Ġval Ġdep Ġ= Ġref . module Info ĊĠĠĠĠĠ Ġif Ġ( is Node Skipped ( dep )) Ġ{ ĊĠĠĠĠĠĠĠ Ġcontinue ĊĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠ Ġif Ġ(! node Info To Id . containsKey ( dep )) Ġ{ ĊĠĠĠĠĠĠĠ Ġwrite Module Info ( writer Ġ= Ġwriter , Ġitem Ġ= Ġdep , ĠparentId Ġ= Ġnull ) ĊĠĠĠĠĠ Ġ} ĊĠĠĠĠĠ Ġdependency Links . compute If Absent ( dependent Id ) Ġ{ Ġmutable ListOf () Ġ}. add ( node Info To Id . get ( dep ) !! ) ĊĠĠĠ Ġ} ĊĠ Ġ} ĊĊĠ Ġprivate Ġfun Ġget ShortName ( name : ĠString ): ĠString Ġ{ ĊĠĠĠ Ġif Ġ( name . startsWith (" intellij .")) Ġ{ ĊĠĠĠĠĠ Ġreturn Ġ" i .${ name . substring (" intellij .". length )}" ĊĠĠĠ Ġ} ĊĠĠĠ Ġelse Ġif Ġ( name . startsWith (" com . intellij . modules .")) Ġ{ ĊĠĠĠĠĠ Ġreturn Ġ" c . i . m .${ name . substring (" com . intellij . modules .". length )}" ĊĠĠĠ Ġ} ĊĠĠĠ Ġelse Ġ{ ĊĠĠĠĠĠ Ġreturn Ġname ĊĠĠĠ Ġ} ĊĠ Ġ} Ċ } Ċ Ċ private Ġfun Ġwrite Links ( writer : ĠJson Generator , Ġlinks : ĠMap < String , ĠList < String >>, Ġis Content : ĠBoolean ) Ġ{ ĊĠ Ġlinks . forEach ( Bi Consumer Ġ{ Ġsource , Ġtargets Ġ-> ĊĠĠĠ Ġfor Ġ( target Ġin Ġtargets ) Ġ{ ĊĠĠĠĠĠ Ġwriter . obj Ġ{ ĊĠĠĠĠĠĠĠ Ġwriter . writeString Field (" group ", Ġ" edges ") ĊĠĠĠĠĠĠĠ Ġwriter . obj (" data ") Ġ{ ĊĠĠĠĠĠĠĠĠĠ Ġwriter . writeString Field (" id ", Ġ"${ source }_${ target }") ĊĠĠĠĠĠĠĠĠĠ Ġwriter . writeString Field (" source ", Ġsource ) ĊĠĠĠĠĠĠĠĠĠ Ġwriter . writeString Field (" target ", Ġtarget ) ĊĠĠĠĠĠĠĠĠĠ Ġwriter . write Number Field (" type ", Ġif Ġ( is Content ) Ġ 1 Ġelse Ġ 0 ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĠ Ġ}) Ċ } Ċ Ċ // Ġskip Ġto Ġsimplify Ġgraph Ċ private Ġfun Ġis Node Skipped ( dep : ĠModule Info ) Ġ= Ġdep . name Ġ== Ġ" com . intellij . modules . ult imate " Ġ|| Ġdep . name Ġ== Ġ" com . intellij . modules . lang " Ċ Ċ private Ġfun ĠgetItem NodeType ( item : ĠModule Info ): ĠInt Ġ{ ĊĠ Ġif Ġ( item . is Plugin ) Ġ{ ĊĠĠĠ Ġreturn Ġif Ġ( item . content . isEmpty ()) Ġ 1 Ġelse Ġ 2 ĊĠ Ġ} ĊĠ Ġelse Ġ{ ĊĠĠĠ Ġreturn Ġ 0 ĊĠ Ġ} Ċ } Ċ