/* inherit -- Inheritance Graph Generator
 * Copyright 2009 Mark Harrah
 */
package inherit

import scala.tools.nsc.{plugins, Global, Phase}
import plugins.{Plugin, PluginComponent}

import scala.collection.jcl.{TreeMap, TreeSet}
import scala.collection.mutable.HashMap
import java.io.{BufferedWriter, File, FileWriter, PrintWriter, Writer}

/** Contains the main code for traversing the AST and producing the inheritance graph. The main method
* is 'graph'.*/
abstract class class Inherit extends scala.tools.nsc.plugins.Plugin with ScalaObjectInherit extends scala.tools.nsc.plugins.PluginPlugin
{
	// global is the compiler instance provided to the plugin
	import global._
	/** The file to write the graph to.*/
	protected def => java.io.Fileoutput: java.io.FileFile
	/** Construct the inheritance graph and write it to 'output'.*/
	def ()Unitgraph()
	{
		val java.io.PrintWriterout = java.io.PrintWriternew java.io.PrintWriterPrintWriter(java.io.BufferedWriternew java.io.BufferedWriterBufferedWriter((java.io.File)java.io.FileWriternew java.io.FileWriterFileWriter(=> java.io.Fileoutput)))
		try
		{
			java.io.PrintWriterout.(java.lang.String)Unitprintln(java.lang.String("digraph Inheritance {")"digraph Inheritance {")
			(java.io.PrintWriter)Unitgraph(java.io.PrintWriterout)
			java.io.PrintWriterout.(java.lang.String)Unitprintln(java.lang.String("}")"}")
		}
		finally { java.io.PrintWriterout.()Unitclose() }
	}
	/** Construct the inheritance graph and write it to 'out'.*/
	private def (java.io.PrintWriter)Unitgraph(java.io.PrintWriterout: java.io.PrintWriterPrintWriter)
	{
		// map from a class/module to its immediate parents
		val scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]subToSuper = scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]new scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]HashMap[Symbol, Set[Symbol]]
		// include all classes/modules directly in the source
		for(((Inherit.this.global.CompilationUnit) => Unit)Unitunit <- => Inherit.this.global.RuncurrentRun.=> Iterator[Inherit.this.global.CompilationUnit]units; ((Inherit.this.global.Tree) => Unit)Unitnode <- Inherit.this.global.CompilationUnitunit.=> Inherit.this.global.Treebody)
		{
			Inherit.this.global.Treenode Unitmatch
			{
				Unitcase Inherit.this.global.ClassDefcd: Inherit.this.global.ClassDefClassDef => (Inherit.this.global.Symbol)UnitprocessDirect(Inherit.this.global.ClassDefcd.=> Inherit.this.global.Symbolsymbol)
				Unitcase Inherit.this.global.ModuleDefmd: Inherit.this.global.ModuleDefModuleDef => (Inherit.this.global.Symbol)UnitprocessDirect(Inherit.this.global.ModuleDefmd.=> Inherit.this.global.Symbolsymbol)
				Unitcase _ => Unit()
			}
		}

		def (Inherit.this.global.Symbol)UnitprocessDirect(Inherit.this.global.Symbolsym: Inherit.this.global.SymbolSymbol): UnitUnit  =  Unitif((Inherit.this.global.Symbol)Booleanvisible(Inherit.this.global.Symbolsym)) (Inherit.this.global.Symbol)Set[Inherit.this.global.Symbol]getParents(Inherit.this.global.Symbolsym)
		def (Inherit.this.global.Symbol)Set[Inherit.this.global.Symbol]getParents(Inherit.this.global.Symbolsym: Inherit.this.global.SymbolSymbol) = scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]subToSuper.(Inherit.this.global.Symbol,=> Set[Inherit.this.global.Symbol])Set[Inherit.this.global.Symbol]getOrElseUpdate(Inherit.this.global.Symbolsym, (Inherit.this.global.Symbol)scala.collection.immutable.Set[Inherit.this.global.Symbol]parents(Inherit.this.global.Symbolsym))
		// alias for an immutable empty set of Symbols
		def => scala.collection.immutable.Set[Inherit.this.global.Symbol]empty = => collection.immutable.Set.typeSet.scala.collection.immutable.Set[Inherit.this.global.Symbol]empty[Inherit.this.global.SymbolSymbol]
		// get the parents of the given symbol if it is a class or module
		def (Inherit.this.global.Symbol)scala.collection.immutable.Set[Inherit.this.global.Symbol]parents(Inherit.this.global.Symboldefinition: Inherit.this.global.SymbolSymbol) =
		{
			val Inherit.this.global.Symbolsym =
				Inherit.this.global.Symbolif(Inherit.this.global.Symboldefinition.=> BooleanisModule) Inherit.this.global.Symboldefinition.=> Inherit.this.global.SymbolmoduleClass // the module class has the parent information
				else Inherit.this.global.Symboldefinition
			
			Inherit.this.global.Symbolsym scala.collection.immutable.Set[Inherit.this.global.Symbol]match
			{
				scala.collection.immutable.Set[Inherit.this.global.Symbol]case Inherit.this.global.ClassSymbolcs: Inherit.this.global.ClassSymbolClassSymbol => (Inherit.this.global.ClassSymbol)scala.collection.immutable.Set[Inherit.this.global.Symbol]classParents(Inherit.this.global.ClassSymbolcs)
				scala.collection.immutable.Set[Inherit.this.global.Symbol]case _ => => scala.collection.immutable.Set[Inherit.this.global.Symbol]empty
			}
		}
		// get the parents of the given class/module
		def (Inherit.this.global.ClassSymbol)scala.collection.immutable.Set[Inherit.this.global.Symbol]classParents(Inherit.this.global.ClassSymbolcs: Inherit.this.global.ClassSymbolClassSymbol) =
		{
			Inherit.this.global.ClassSymbolcs.=> Inherit.this.global.Typeinfo scala.collection.immutable.Set[Inherit.this.global.Symbol]match
			{
				scala.collection.immutable.Set[Inherit.this.global.Symbol]case Inherit.this.global.ClassInfoTypecit: Inherit.this.global.ClassInfoTypeClassInfoType => (Inherit.this.global.ClassInfoType)scala.collection.immutable.Set[Inherit.this.global.Symbol]parentsFromClassInfo(Inherit.this.global.ClassInfoTypecit)
				scala.collection.immutable.Set[Inherit.this.global.Symbol]case PolyType(_, Inherit.this.global.Typeresult) =>
					Inherit.this.global.Typeresult scala.collection.immutable.Set[Inherit.this.global.Symbol]match
					{
						scala.collection.immutable.Set[Inherit.this.global.Symbol]case Inherit.this.global.ClassInfoTypecit: Inherit.this.global.ClassInfoTypeClassInfoType => (Inherit.this.global.ClassInfoType)scala.collection.immutable.Set[Inherit.this.global.Symbol]parentsFromClassInfo(Inherit.this.global.ClassInfoTypecit)
						scala.collection.immutable.Set[Inherit.this.global.Symbol]case _ => => scala.collection.immutable.Set[Inherit.this.global.Symbol]empty
					}
				scala.collection.immutable.Set[Inherit.this.global.Symbol]case _ => => scala.collection.immutable.Set[Inherit.this.global.Symbol]empty
			}
		}
		// get the parents from the class info
		def (Inherit.this.global.ClassInfoType)scala.collection.immutable.Set[Inherit.this.global.Symbol]parentsFromClassInfo(Inherit.this.global.ClassInfoTypecit: Inherit.this.global.ClassInfoTypeClassInfoType) = (Inherit.this.global.Symbol*)scala.collection.immutable.Set[Inherit.this.global.Symbol]Set(Inherit.this.global.ClassInfoTypecit.=> List[Inherit.this.global.Type]parents.((Inherit.this.global.Type) => Inherit.this.global.Symbol)List[Inherit.this.global.Symbol]map(Inherit.this.global.Type_.=> Inherit.this.global.SymboltypeSymbol).((Inherit.this.global.Symbol) => Boolean)List[Inherit.this.global.Symbol]filter((Inherit.this.global.Symbol)BooleanfilterClass) : _*)

		// all ancestors of a class or module
		val scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]ancestors = scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]new scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]HashMap[Symbol, Set[Symbol]]
		// get the ancestors for the given symbol (recomputed each call)
		def (Inherit.this.global.Symbol)Set[Inherit.this.global.Symbol]ancestorsImpl(Inherit.this.global.Symbolsym: Inherit.this.global.SymbolSymbol): Set[Inherit.this.global.Symbol]Set[Symbol] =
		{
			val Set[Inherit.this.global.Symbol]parents = (Inherit.this.global.Symbol)Set[Inherit.this.global.Symbol]getParents(Inherit.this.global.Symbolsym)
			Set[Inherit.this.global.Symbol]parents.((Inherit.this.global.Symbol) => Iterable[Inherit.this.global.Symbol])scala.collection.immutable.Set[Inherit.this.global.Symbol]flatMap(Inherit.this.global.Symbolparent => (Inherit.this.global.Symbol)Set[Inherit.this.global.Symbol]getAncestors(Inherit.this.global.Symbolparent)) (Iterable[Inherit.this.global.Symbol])scala.collection.immutable.Set[Inherit.this.global.Symbol]++ Set[Inherit.this.global.Symbol]parents
		}
		// get the ancestors for the given symbol (memoized)
		def (Inherit.this.global.Symbol)Set[Inherit.this.global.Symbol]getAncestors(Inherit.this.global.Symbolsym: Inherit.this.global.SymbolSymbol) = scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]ancestors.(Inherit.this.global.Symbol,=> Set[Inherit.this.global.Symbol])Set[Inherit.this.global.Symbol]getOrElseUpdate(Inherit.this.global.Symbolsym, (Inherit.this.global.Symbol)Set[Inherit.this.global.Symbol]ancestorsImpl(Inherit.this.global.Symbolsym))
		// fill in the ancestors for all classes we are looking at
		scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]subToSuper.=> scala.collection.Set[Inherit.this.global.Symbol]keySet.((Inherit.this.global.Symbol) => Unit)Unitforeach((Inherit.this.global.Symbol)Set[Inherit.this.global.Symbol]getAncestors)

		// write out the definitions/attributes for each main class/module
		scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]ancestors.=> scala.collection.Set[Inherit.this.global.Symbol]keySet.((Inherit.this.global.Symbol) => Unit)Unitforeach(Inherit.this.global.Symbolsym => Unitif((Inherit.this.global.Symbol)Booleanvisible(Inherit.this.global.Symbolsym)) java.io.PrintWriterout.(java.lang.String)Unitprintln((Inherit.this.global.Symbol)java.lang.StringmakeLabel(Inherit.this.global.Symbolsym)))

		// write out the directed edges to parents
		for( (((Inherit.this.global.Symbol, Set[Inherit.this.global.Symbol])) => Unit)Unit(Inherit.this.global.Symbolsymbol, Set[Inherit.this.global.Symbol]parents) <- scala.collection.mutable.HashMap[Inherit.this.global.Symbol,Set[Inherit.this.global.Symbol]]subToSuper if (Inherit.this.global.Symbol)Booleanvisible(Inherit.this.global.Symbolsymbol))
		{
			val Set[Inherit.this.global.Symbol]indirectAncestors: Set[Inherit.this.global.Symbol]Set[Symbol] = Set[Inherit.this.global.Symbol]parents.((Inherit.this.global.Symbol) => Iterable[Inherit.this.global.Symbol])scala.collection.immutable.Set[Inherit.this.global.Symbol]flatMap((Inherit.this.global.Symbol)Set[Inherit.this.global.Symbol]getAncestors)
			val scala.collection.immutable.Set[Inherit.this.global.Symbol]uniqueParents = Set[Inherit.this.global.Symbol]parents.((Inherit.this.global.Symbol) => Boolean)scala.collection.immutable.Set[Inherit.this.global.Symbol]filter(Inherit.this.global.Symbolparent => => Boolean!Set[Inherit.this.global.Symbol]indirectAncestors.(Inherit.this.global.Symbol)Booleancontains(Inherit.this.global.Symbolparent))
			for(((Inherit.this.global.Symbol) => Unit)Unitparent <- scala.collection.immutable.Set[Inherit.this.global.Symbol]uniqueParents)
				java.io.PrintWriterout.(java.lang.String)Unitprintln(Inherit.this.global.Symbolsymbol.=> Intid (java.lang.String)java.lang.String+ java.lang.String(" -> ")" -> " (Any)java.lang.String+ Inherit.this.global.Symbolparent.=> Intid)
		}
	}
	// true if the given symbol is not anonymous
	private def (Inherit.this.global.Symbol)Booleanvisible(Inherit.this.global.Symbolsym: Inherit.this.global.SymbolSymbol) = => Boolean!Inherit.this.global.Symbolsym.=> BooleanisAnonymousFunction (Boolean)Boolean&& => Boolean!Inherit.this.global.Symbolsym.=> BooleanisAnonymousClass
	// converts the symbol into a readable representation, TODO: decode symbols in names
	private def (Inherit.this.global.Symbol)StringtoString(Inherit.this.global.Symbolsym: Inherit.this.global.SymbolSymbol) = Inherit.this.global.Symbolsym.=> StringfullNameString
	// produce a definition string for the graph file
	private def (Inherit.this.global.Symbol)java.lang.StringmakeLabel(Inherit.this.global.Symbolsym: Inherit.this.global.SymbolSymbol) =
	{
		val java.lang.Stringlabel = (java.lang.Stringif(Inherit.this.global.Symbolsym.=> BooleanisModule) java.lang.String("object ")"object " else java.lang.String("class ")"class ") (Any)java.lang.String+ (Inherit.this.global.Symbol)StringtoString(Inherit.this.global.Symbolsym)
		Inherit.this.global.Symbolsym.=> Intid (java.lang.String)java.lang.String+ java.lang.String("[shape=box label="")"[shape=box label=\"" (Any)java.lang.String+ java.lang.Stringlabel (Any)java.lang.String+ java.lang.String(""]")"\"]"
	}
	private def (Inherit.this.global.Symbol)BooleanfilterClass(Inherit.this.global.Symbolsym: Inherit.this.global.SymbolSymbol) = (String)BooleanfilterClassName((Inherit.this.global.Symbol)StringtoString(Inherit.this.global.Symbolsym))
	private def (String)BooleanfilterClassName(Stringname: StringString) = Boolean(true)true//name != "scala.Product" && name != "scala.ScalaObject"
}

/****** Standard compiler plugin configuration. **********/

object object inherit.InheritPluginInheritPlugin
{
	val java.lang.StringPluginName = java.lang.String("inherit")"inherit"
}
class class InheritPlugin extends inherit.Inherit with ScalaObjectInheritPlugin(val scala.tools.nsc.Globalglobal: scala.tools.nsc.GlobalGlobal) extends inherit.InheritInherit
{
	import global._
	import InheritPlugin._
	
	val java.lang.Stringname = => java.lang.StringPluginName
	val java.lang.Stringdescription = java.lang.String("A plugin to produce an inheritance graph of the input sources.")"A plugin to produce an inheritance graph of the input sources."
	val List[scala.tools.nsc.plugins.PluginComponent]components = (scala.tools.nsc.plugins.PluginComponent*)List[scala.tools.nsc.plugins.PluginComponent]List[scala.tools.nsc.plugins.PluginComponentPluginComponent](object InheritPlugin.this.ComponentComponent)
	
	/** The output file, currently hard-coded. */
	val java.io.Fileoutput = ((java.lang.String,java.lang.String)java.io.Filenew java.io.FileFile(=> scala.tools.nsc.Settingssettings.=> scala.tools.nsc.Settings#StringSettingoutdir.=> Stringvalue, java.lang.String("../inherit/inherit.dot")"../inherit/inherit.dot")).()java.io.FilegetAbsoluteFile
	=> java.io.Fileoutput.()java.io.FilegetParentFile.()Booleanmkdirs()

	/* For source compatibility between 2.7.x and 2.8.x */
	private object object InheritPlugin.this.runsBeforerunsBefore { def (String)String:: (Strings: StringString) = Strings }
	private abstract class class CompatiblePluginComponent extends scala.tools.nsc.plugins.PluginComponent with ScalaObjectCompatiblePluginComponent(StringafterPhase: StringString) extends scala.tools.nsc.plugins.PluginComponentPluginComponent
	{
		val StringrunsAfter = StringafterPhase (String)String:: object InheritPlugin.this.runsBeforerunsBefore
	}
	private object object InheritPlugin.this.ComponentComponent extends InheritPlugin.this.CompatiblePluginComponentCompatiblePluginComponent(java.lang.String("typer")"typer")
	{
		val scala.tools.nsc.Globalglobal = InheritPlugin.this.typeInheritPlugin.this.=> scala.tools.nsc.Globalglobal
		val java.lang.StringphaseName = InheritPlugin.this.typeInheritPlugin.this.=> java.lang.Stringname
		def (scala.tools.nsc.Phase)InheritPlugin.this.InheritPhasenewPhase(scala.tools.nsc.Phaseprev: scala.tools.nsc.PhasePhase) = InheritPlugin.this.InheritPhasenew InheritPlugin.this.InheritPhaseInheritPhase(scala.tools.nsc.Phaseprev)
	}

	private class class InheritPhase extends scala.tools.nsc.Phase with ScalaObjectInheritPhase(scala.tools.nsc.Phaseprev: scala.tools.nsc.PhasePhase) extends scala.tools.nsc.PhasePhase(scala.tools.nsc.Phaseprev)
	{
		def => java.lang.Stringname = InheritPlugin.this.typeInheritPlugin.this.=> java.lang.Stringname
		def => Unitrun = ()Unitgraph()
	}
}