/* sbt -- Simple Build Tool
 * Copyright 2008, 2009 Mark Harrah
 */
package sbt

import scala.tools.nsc.{io, plugins, symtab, Global, Phase}
import io.{AbstractFile, PlainFile, ZipArchive}
import plugins.{Plugin, PluginComponent}
import symtab.Flags
import scala.collection.mutable.{HashMap, HashSet, Map, Set}

import java.io.File

object object sbt.AnalyzerAnalyzer
{
	val java.lang.StringPluginName = java.lang.String("sbt-analyzer")"sbt-analyzer"
	val java.lang.StringCallbackIDOptionName = java.lang.String("callback:")"callback:"
}
class class Analyzer extends scala.tools.nsc.plugins.Plugin with ScalaObjectAnalyzer(val scala.tools.nsc.Globalglobal: scala.tools.nsc.GlobalGlobal) extends scala.tools.nsc.plugins.PluginPlugin
{
	import global._
	import Analyzer._
	
	val java.lang.Stringname = => java.lang.StringPluginName
	val java.lang.Stringdescription = java.lang.String("A plugin to find all concrete instances of a given class and extract dependency information.")"A plugin to find all concrete instances of a given class and extract dependency information."
	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 Analyzer.this.ComponentComponent)
	
	private var Option[sbt.AnalysisCallback]callbackOption: Option[sbt.AnalysisCallback]Option[AnalysisCallback] = object NoneNone
	
	override def (List[String],(String) => Unit)UnitprocessOptions(List[String]options: List[String]List[String], (String) => Uniterror: String => Unit)
	{
		for(((String) => Unit)Unitoption <- List[String]options)
		{
			Unitif(Stringoption.(java.lang.String)BooleanstartsWith(=> java.lang.StringCallbackIDOptionName))
				(Option[sbt.AnalysisCallback])UnitcallbackOption = (Int)Option[sbt.AnalysisCallback]AnalysisCallback(Stringoption.implicit scala.Predef.stringWrapper : (String)scala.runtime.RichStringsubstring(=> java.lang.StringCallbackIDOptionName.()Intlength).=> InttoInt)
			else
				(String)Uniterror(java.lang.String("Option not understood: ")"Option not understood: " (Any)java.lang.String+ Stringoption)
		}
		Unitif(=> Option[sbt.AnalysisCallback]callbackOption.=> BooleanisEmpty)
			(String)Uniterror(java.lang.String("Callback ID not specified.")"Callback ID not specified.")
	}

	override val Option[String]optionsHelp: Option[String]Option[String] =
	{
		val java.lang.Stringprefix = java.lang.String("  -P:")"  -P:" (Any)java.lang.String+ => java.lang.Stringname (Any)java.lang.String+ java.lang.String(":")":"
		(java.lang.String)Some[java.lang.String]Some(java.lang.Stringprefix (Any)java.lang.String+ => java.lang.StringCallbackIDOptionName (Any)java.lang.String+ java.lang.String("<callback-id>            Set the callback id.\012")"<callback-id>            Set the callback id.\n")
	}

	/* ================================================== */
	// These two templates abuse scope for source compatibility between Scala 2.7.x and 2.8.x so that a single
	// sbt codebase compiles with both series of versions.
	// In 2.8.x, PluginComponent.runsAfter has type List[String] and the method runsBefore is defined on
	//   PluginComponent with default value Nil.
	// In 2.7.x, runsBefore does not exist on PluginComponent and PluginComponent.runsAfter has type String.
	//
	// Therefore, in 2.8.x, object runsBefore is shadowed by PluginComponent.runsBefore (which is Nil) and so
	//   afterPhase :: runsBefore
	// is equivalent to List[String](afterPhase)
	// In 2.7.x, object runsBefore is not shadowed and so runsAfter has type String.
	private object object Analyzer.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
	{
		override val List[String]runsAfter = StringafterPhase (String)List[String]:: => List[String]runsBefore
	}
	/* ================================================== */
	
	private object object Analyzer.this.ComponentComponent extends Analyzer.this.CompatiblePluginComponentCompatiblePluginComponent(java.lang.String("jvm")"jvm")
	{
		val scala.tools.nsc.Globalglobal = Analyzer.this.typeAnalyzer.this.=> scala.tools.nsc.Globalglobal
		val java.lang.StringphaseName = Analyzer.this.typeAnalyzer.this.=> java.lang.Stringname
		def (scala.tools.nsc.Phase)Analyzer.this.AnalyzerPhasenewPhase(scala.tools.nsc.Phaseprev: scala.tools.nsc.PhasePhase) = Analyzer.this.AnalyzerPhasenew Analyzer.this.AnalyzerPhaseAnalyzerPhase(scala.tools.nsc.Phaseprev)
	}

	private class class AnalyzerPhase extends scala.tools.nsc.Phase with ScalaObjectAnalyzerPhase(scala.tools.nsc.Phaseprev: scala.tools.nsc.PhasePhase) extends scala.tools.nsc.PhasePhase(scala.tools.nsc.Phaseprev)
	{
		def => java.lang.Stringname = Analyzer.this.typeAnalyzer.this.=> java.lang.Stringname
		def => Unitrun
		{
			val sbt.AnalysisCallbackcallback = => Option[sbt.AnalysisCallback]callbackOption.=> sbt.AnalysisCallbackget
			val sbt.PathprojectPath = sbt.AnalysisCallbackcallback.=> sbt.PathbasePath
			val StringprojectPathString = object sbt.PathPath.(sbt.Path)Option[String]basePathString(sbt.PathprojectPath).(=> String)StringgetOrElse({(String)Uniterror(java.lang.String("Could not determine base path for ")"Could not determine base path for " (Any)java.lang.String+ sbt.PathprojectPath); java.lang.String("")""})
			def (java.io.File)Option[sbt.Path]relativize(java.io.Filefile: java.io.FileFile) = object sbt.PathPath.(sbt.Path,String,java.io.File)Option[sbt.Path]relativize(sbt.PathprojectPath, StringprojectPathString, java.io.Filefile)
			
			val java.io.FileoutputDir = java.io.Filenew java.io.FileFile(=> scala.tools.nsc.Globalglobal.=> scala.tools.nsc.Settingssettings.=> scala.tools.nsc.Settings.StringSettingoutdir.=> scala.tools.nsc.Settings.StringSetting#Tvalue)
			val Option[sbt.Path]outputPathOption = (java.io.File)Option[sbt.Path]relativize(java.io.FileoutputDir)
			Unitif(Option[sbt.Path]outputPathOption.=> BooleanisEmpty)
				(String)Uniterror(java.lang.String("Output directory ")"Output directory " (Any)java.lang.String+ java.io.FileoutputDir.()java.lang.StringgetAbsolutePath (Any)java.lang.String+ java.lang.String(" must be in the project directory.")" must be in the project directory.")
			val sbt.PathoutputPath = Option[sbt.Path]outputPathOption.=> sbt.Pathget
			
			val Iterable[Analyzer.this.global.Name]superclassNames = sbt.AnalysisCallbackcallback.=> Iterable[String]superclassNames.((String) => Analyzer.this.global.Name)Iterable[Analyzer.this.global.Name]map((String)Analyzer.this.global.NamenewTermName)
			val Iterable[Option[Analyzer.this.global.Symbol]]superclassesAll =
				for(((Analyzer.this.global.Name) => Option[Analyzer.this.global.Symbol])Iterable[Option[Analyzer.this.global.Symbol]]name <- Iterable[Analyzer.this.global.Name]superclassNames) yield
				{
					try { (Analyzer.this.global.Symbol)Some[Analyzer.this.global.Symbol]Some(=> scala.tools.nsc.Globalglobal.object Analyzer.this.global.definitionsdefinitions.(Analyzer.this.global.Name)Analyzer.this.global.SymbolgetClass(Analyzer.this.global.Namename)) }
					catch { None.typecase scala.tools.nsc.FatalErrorfe: scala.tools.nsc.scala.tools.nsc.FatalErrorFatalError => sbt.AnalysisCallbackcallback.(String)UnitsuperclassNotFound(Analyzer.this.global.Namename.()StringtoString); object NoneNone }
				}
			val Iterable[Analyzer.this.global.Symbol]superclasses = Iterable[Option[Analyzer.this.global.Symbol]]superclassesAll.((Option[Analyzer.this.global.Symbol]) => Boolean)Iterable[Option[Analyzer.this.global.Symbol]]filter(Option[Analyzer.this.global.Symbol]_.=> BooleanisDefined).((Option[Analyzer.this.global.Symbol]) => Analyzer.this.global.Symbol)Iterable[Analyzer.this.global.Symbol]map(Option[Analyzer.this.global.Symbol]_.=> Analyzer.this.global.Symbolget)
			
			for(((Analyzer.this.global.CompilationUnit) => Unit)Unitunit <- => Analyzer.this.global.RuncurrentRun.=> Iterator[Analyzer.this.global.CompilationUnit]units)
			{
				// build dependencies structure
				val java.io.FilesourceFile = Analyzer.this.global.CompilationUnitunit.=> scala.tools.nsc.util.SourceFilesource.=> scala.tools.nsc.io.AbstractFilefile.=> java.io.Filefile
				val Option[sbt.Path]sourcePathOption = (java.io.File)Option[sbt.Path]relativize(java.io.FilesourceFile)
				Unitif(Option[sbt.Path]sourcePathOption.=> BooleanisEmpty)
					(String)Uniterror(java.lang.String("Source file ")"Source file " (Any)java.lang.String+ java.io.FilesourceFile.()java.lang.StringgetAbsolutePath (Any)java.lang.String+ java.lang.String(" must be in the project directory.")" must be in the project directory.")
				val sbt.PathsourcePath = Option[sbt.Path]sourcePathOption.=> sbt.Pathget
				sbt.AnalysisCallbackcallback.(sbt.Path)UnitbeginSource(sbt.PathsourcePath)
				for(((Analyzer.this.global.Symbol) => Unit)Uniton <- Analyzer.this.global.CompilationUnitunit.=> scala.collection.mutable.HashSet[Analyzer.this.global.Symbol]depends)
				{
					val scala.tools.nsc.io.AbstractFileonSource = Analyzer.this.global.Symbolon.=> scala.tools.nsc.io.AbstractFilesourceFile
					Unitif(scala.tools.nsc.io.AbstractFileonSource (AnyRef)Boolean== Null(null)null)
					{
						(Analyzer.this.global.Symbol)Option[scala.tools.nsc.io.AbstractFile]classFile(Analyzer.this.global.Symbolon) Unitmatch
						{
							Unitcase Some(scala.tools.nsc.io.AbstractFilef) =>
							{
								scala.tools.nsc.io.AbstractFilef Unitmatch
								{
									Unitcase scala.tools.nsc.io.ZipArchive#Entryze: ZipArchive#scala.tools.nsc.io.ZipArchive#EntryEntry => sbt.AnalysisCallbackcallback.(java.io.File,sbt.Path)UnitjarDependency(java.io.Filenew java.io.FileFile(scala.tools.nsc.io.ZipArchive#Entryze.=> java.util.zip.ZipFilegetArchive.()java.lang.StringgetName), sbt.PathsourcePath)
									Unitcase scala.tools.nsc.io.PlainFilepf: scala.tools.nsc.io.PlainFilePlainFile =>
									{
										 // ignore dependencies in the output directory: these are handled by source dependencies
										Unitif(object sbt.PathPath.(sbt.Path,java.io.File)Option[sbt.Path]relativize(sbt.PathoutputPath, scala.tools.nsc.io.PlainFilepf.=> java.io.Filefile).=> BooleanisEmpty)
											sbt.AnalysisCallbackcallback.(java.io.File,sbt.Path)UnitclassDependency(scala.tools.nsc.io.PlainFilepf.=> java.io.Filefile, sbt.PathsourcePath)
									}
									Unitcase _ => Unit()
								}
							}
							Unitcase object NoneNone => Unit()
						}
					}
					else
					{
						for(((sbt.Path) => Unit)UnitdepPath <- (java.io.File)Option[sbt.Path]relativize(scala.tools.nsc.io.AbstractFileonSource.=> java.io.Filefile))
							sbt.AnalysisCallbackcallback.(sbt.Path,sbt.Path)UnitsourceDependency(sbt.PathdepPath, sbt.PathsourcePath)
					}
				}
				
				// find subclasses
				for(((Analyzer.this.global.Tree) => Unit)Unitclazz @ ClassDef(Analyzer.this.global.Modifiersmods, Analyzer.this.global.Namen, _, _) <- Analyzer.this.global.CompilationUnitunit.=> Analyzer.this.global.Treebody)
				{
					val Analyzer.this.global.Symbolsym = Analyzer.this.global.ClassDefclazz.=> Analyzer.this.global.Symbolsymbol
					Unitif(Analyzer.this.global.Modifiersmods.=> BooleanisPublic (Boolean)Boolean&& => Boolean!Analyzer.this.global.Modifiersmods.=> BooleanisAbstract (Boolean)Boolean&& => Boolean!Analyzer.this.global.Modifiersmods.=> BooleanisTrait (Boolean)Boolean&&
						 => Boolean!Analyzer.this.global.Symbolsym.=> BooleanisImplClass (Boolean)Boolean&& Analyzer.this.global.Symbolsym.=> BooleanisStatic (Boolean)Boolean&& => Boolean!Analyzer.this.global.Symbolsym.=> BooleanisNestedClass)
					{
						val BooleanisModule = Analyzer.this.global.Symbolsym.=> BooleanisModuleClass
						for(((Analyzer.this.global.Symbol) => Unit)Unitsuperclass <- Iterable[Analyzer.this.global.Symbol]superclasses.((Analyzer.this.global.Symbol) => Boolean)Iterable[Analyzer.this.global.Symbol]filter(Analyzer.this.global.Symbolsym.(Analyzer.this.global.Symbol)BooleanisSubClass))
							sbt.AnalysisCallbackcallback.(sbt.Path,String,String,Boolean)UnitfoundSubclass(sbt.PathsourcePath, Analyzer.this.global.Symbolsym.=> StringfullNameString, Analyzer.this.global.Symbolsuperclass.=> StringfullNameString, BooleanisModule)
					}
				}
				
				// build list of generated classes
				for(((Analyzer.this.global.icodes.IClass) => Unit)Uniticlass <- Analyzer.this.global.CompilationUnitunit.=> scala.collection.mutable.HashSet[Analyzer.this.global.icodes.IClass]icode)
				{
					val Analyzer.this.global.icodes.global.Symbolsym = Analyzer.this.global.icodes.IClassiclass.=> Analyzer.this.global.icodes.global.Symbolsymbol
					def (Boolean)UnitaddGenerated(BooleanseparatorRequired: BooleanBoolean)
					{
						val sbt.PathclassPath = (sbt.Path,Analyzer.this.global.Symbol,Boolean)sbt.PathpathOfClass(sbt.PathoutputPath, Analyzer.this.global.icodes.global.Symbolsym, BooleanseparatorRequired)
						Unitif(sbt.PathclassPath.=> java.io.FileasFile.()Booleanexists)
							sbt.AnalysisCallbackcallback.(sbt.Path,sbt.Path)UnitgeneratedClass(sbt.PathsourcePath, sbt.PathclassPath)
					}
					Unitif(Analyzer.this.global.icodes.global.Symbolsym.=> BooleanisModuleClass (Boolean)Boolean&& => Boolean!Analyzer.this.global.icodes.global.Symbolsym.=> BooleanisImplClass)
					{
						Unitif((Analyzer.this.global.Symbol)BooleanisTopLevelModule(Analyzer.this.global.icodes.global.Symbolsym) (Boolean)Boolean&& Analyzer.this.global.icodes.global.Symbolsym.=> Analyzer.this.global.icodes.global.SymbollinkedClassOfModule (AnyRef)Boolean== object Analyzer.this.global.NoSymbolNoSymbol)
							(Boolean)UnitaddGenerated(Boolean(false)false)
						(Boolean)UnitaddGenerated(Boolean(true)true)
					}
					else
						(Boolean)UnitaddGenerated(Boolean(false)false)
				}
				sbt.AnalysisCallbackcallback.(sbt.Path)UnitendSource(sbt.PathsourcePath)
			}
		}
	}
	
	private def (Analyzer.this.global.Symbol)Option[scala.tools.nsc.io.AbstractFile]classFile(Analyzer.this.global.Symbolsym: Analyzer.this.global.SymbolSymbol): Option[scala.tools.nsc.io.AbstractFile]Option[AbstractFile] =
	{
		import scala.tools.nsc.symtab.Flags
		val java.lang.Stringname = Analyzer.this.global.Symbolsym.(Char)StringfullNameString(java.io.object java.io.FileFile.CharseparatorChar) (Any)java.lang.String+ (Anyif (Analyzer.this.global.Symbolsym.(Long)BooleanhasFlag(Flags.Long(256L)MODULE)) java.lang.String("$")"$" else java.lang.String("")"")
		val Analyzer.this.global.classPath0.Contextentry = => Analyzer.this.global.classPath0.BuildclassPath.=> Analyzer.this.global.classPath0.Contextroot.(String,Boolean)Analyzer.this.global.classPath0.Contextfind(java.lang.Stringname, Boolean(false)false)
		Option[scala.tools.nsc.io.AbstractFile]if (Analyzer.this.global.classPath0.Contextentry (AnyRef)Booleanne Null(null)null)
			(scala.tools.nsc.io.AbstractFile)Some[scala.tools.nsc.io.AbstractFile]Some(Analyzer.this.global.classPath0.Contextentry.=> scala.tools.nsc.io.AbstractFileclassFile)
		else
			object NoneNone
	}
	
	private def (Analyzer.this.global.Symbol)BooleanisTopLevelModule(Analyzer.this.global.Symbolsym: Analyzer.this.global.SymbolSymbol): BooleanBoolean =
		(scala.tools.nsc.Phase)(=> Boolean)BooleanatPhase (=> Analyzer.this.global.RuncurrentRun.=> scala.tools.nsc.PhasepicklerPhase.=> scala.tools.nsc.Phasenext) {
			Analyzer.this.global.Symbolsym.=> BooleanisModuleClass (Boolean)Boolean&& => Boolean!Analyzer.this.global.Symbolsym.=> BooleanisImplClass (Boolean)Boolean&& => Boolean!Analyzer.this.global.Symbolsym.=> BooleanisNestedClass
		}
	
	private def (sbt.Path,Analyzer.this.global.Symbol,Boolean)sbt.PathpathOfClass(sbt.PathoutputPath: sbt.PathPath, Analyzer.this.global.Symbols: Analyzer.this.global.SymbolSymbol, BooleanseparatorRequired: BooleanBoolean): sbt.PathPath =
		(sbt.Path,Analyzer.this.global.Symbol,Boolean,String)sbt.PathpathOfClass(sbt.PathoutputPath, Analyzer.this.global.Symbols, BooleanseparatorRequired, java.lang.String(".class")".class")
	private def (sbt.Path,Analyzer.this.global.Symbol,Boolean,String)sbt.PathpathOfClass(sbt.PathoutputPath: sbt.PathPath, Analyzer.this.global.Symbols: Analyzer.this.global.SymbolSymbol, BooleanseparatorRequired: BooleanBoolean, Stringpostfix: StringString): sbt.PathPath =
	{
		sbt.Pathif(Analyzer.this.global.Symbols.=> Analyzer.this.global.Symbolowner.=> BooleanisPackageClass (Boolean)Boolean&& Analyzer.this.global.Symbols.=> BooleanisPackageClass)
			(sbt.Path,Analyzer.this.global.Symbol)sbt.PathpackagePath(sbt.PathoutputPath, Analyzer.this.global.Symbols) (String)sbt.Path/ Stringpostfix
		else
			(sbt.Path,Analyzer.this.global.Symbol,Boolean,String)sbt.PathpathOfClass(sbt.PathoutputPath, Analyzer.this.global.Symbols.=> Analyzer.this.global.Symbolowner.=> Analyzer.this.global.SymbolenclClass, Boolean(true)true, Analyzer.this.global.Symbols.implicit scala.Predef.any2stringadd : (Any)scala.runtime.StringAddsimpleName (String)java.lang.String+ (Stringif(BooleanseparatorRequired) java.lang.String("$")"$" else java.lang.String("")"") (Any)java.lang.String+ Stringpostfix)
	}
	private def (sbt.Path,Analyzer.this.global.Symbol)sbt.PathpackagePath(sbt.PathoutputPath: sbt.PathPath, Analyzer.this.global.Symbols: Analyzer.this.global.SymbolSymbol): sbt.PathPath =
	{
		sbt.Pathif(Analyzer.this.global.Symbols.=> BooleanisEmptyPackageClass (Boolean)Boolean|| Analyzer.this.global.Symbols.=> BooleanisRoot)
			sbt.PathoutputPath
		else
			(sbt.Path,Analyzer.this.global.Symbol)sbt.PathpackagePath(sbt.PathoutputPath, Analyzer.this.global.Symbols.=> Analyzer.this.global.Symbolowner.=> Analyzer.this.global.SymbolenclClass) (String)sbt.Path/ Analyzer.this.global.Symbols.=> Analyzer.this.global.NamesimpleName.()StringtoString
	}
}