/* sbt -- Simple Build Tool
 * Copyright 2008 Mark Harrah
 *
 * Partially based on exit trapping in Nailgun by Pete Kirkham,
 * copyright 2004, Martian Software, Inc
 * licensed under Apache 2.0 License.
 */
package sbt

import scala.collection.Set

/** This provides functionality to catch System.exit calls to prevent the JVM from terminating.
* This is useful for executing user code that may call System.exit, but actually exiting is
* undesirable.  This file handles the call to exit by disposing all top-level windows and interrupting
* all user started threads.  It does not stop the threads and does not call shutdown hooks.  It is
* therefore inappropriate to use this with code that requires shutdown hooks or creates threads that
* do not terminate.  This category of code should only be called by forking the JVM. */
object object sbt.TrapExitTrapExit
{
	/** Executes the given thunk in a context where System.exit(code) throws
	* a custom SecurityException, which is then caught and the exit code returned
	* in Left.  Otherwise, the result of calling execute is returned. No other
	* exceptions are handled by this method.*/
	def [T](=> T,sbt.Logger)Either[Int,T]apply[>: Nothing <: AnyT](=> Texecute: => T, sbt.Loggerlog: sbt.LoggerLogger): Either[Int,T]Either[Int, T] =
	{
		val java.lang.SecurityManageroriginalSecurityManager = object java.lang.SystemSystem.()java.lang.SecurityManagergetSecurityManager
		val sbt.TrapExitSecurityManagernewSecurityManager = sbt.TrapExitSecurityManagernew sbt.TrapExitSecurityManagerTrapExitSecurityManager(java.lang.SecurityManageroriginalSecurityManager)
		object java.lang.SystemSystem.(java.lang.SecurityManager)UnitsetSecurityManager(sbt.TrapExitSecurityManagernewSecurityManager)
		
		/** Take a snapshot of the threads that existed before execution in order to determine
		* the threads that were created by 'execute'.*/
		val scala.collection.Set[java.lang.Thread]originalThreads = => scala.collection.Set[java.lang.Thread]allThreads
		
		val <refinement> extends Either[Int,T] with Productresult =
			try { (T)Right[Nothing,T]Right(=> Texecute) }
			catch { Left[Int,Nothing]case java.lang.Throwablet: ThrowableThrowable => (Int)Left[Int,Nothing]Left((Throwable)IntexitCode(java.lang.Throwablet)) }
		val <refinement> extends Either[Int,T] with ProductnewResult =
			<refinement> extends Either[Int,T] with Productresult <refinement> extends Either[Int,T] with Productmatch
			{
				<refinement> extends Either[Int,T] with Productcase Right(Tr) =>
				{
					/** In this case, the code completed without calling System.exit.  It possibly started other threads
					* and so we replace the uncaught exception handlers on those threads to handle System.exit.  Then we
					* wait for those threads to die.*/
					val sbt.ExitCodecode = sbt.ExitCodenew sbt.ExitCodeExitCode
					(scala.collection.Set[java.lang.Thread],(java.lang.Thread) => Unit)UnitprocessThreads(scala.collection.Set[java.lang.Thread]originalThreads, java.lang.Threadthread => (java.lang.Thread,scala.collection.Set[java.lang.Thread],sbt.ExitCode)UnitreplaceHandler(java.lang.Threadthread, scala.collection.Set[java.lang.Thread]originalThreads, sbt.ExitCodecode))
					(scala.collection.Set[java.lang.Thread],(java.lang.Thread) => Unit)UnitprocessThreads(scala.collection.Set[java.lang.Thread]originalThreads, java.lang.Threadthread => (java.lang.Thread,sbt.Logger)UnitwaitOnThread(java.lang.Threadthread, sbt.Loggerlog))
					sbt.ExitCodecode.=> Option[Int]value <refinement> extends Either[Int,T] with Productmatch
					{
						Left[Int,Nothing]case Some(IntexitCode) => (Int)Left[Int,Nothing]Left(IntexitCode)
						<refinement> extends Either[Int,T] with Productcase object NoneNone => <refinement> extends Either[Int,T] with Productresult
					}
				}
				<refinement> extends Either[Int,T] with Productcase Left(Intcode) =>
				{
					/** The other case is that the code directly called System.exit.  In this case, we dispose all
					* top-level windows and interrupt user-started threads.*/
					(scala.collection.Set[java.lang.Thread])UnitstopAll(scala.collection.Set[java.lang.Thread]originalThreads)
					<refinement> extends Either[Int,T] with Productresult
				}
			}
			
		object java.lang.SystemSystem.(java.lang.SecurityManager)UnitsetSecurityManager(java.lang.SecurityManageroriginalSecurityManager)

		<refinement> extends Either[Int,T] with ProductnewResult
	}
	/** Waits for the given thread to exit. */
	private def (java.lang.Thread,sbt.Logger)UnitwaitOnThread(java.lang.Threadthread: java.lang.ThreadThread, sbt.Loggerlog: sbt.LoggerLogger)
	{
		sbt.Loggerlog.(=> String)Unitdebug(java.lang.String("Waiting for thread ")"Waiting for thread " (Any)java.lang.String+ java.lang.Threadthread.()java.lang.StringgetName (Any)java.lang.String+ java.lang.String(" to exit")" to exit")
		java.lang.Threadthread.()Unitjoin
		sbt.Loggerlog.(=> String)Unitdebug(java.lang.String("\011Thread ")"\tThread " (Any)java.lang.String+ java.lang.Threadthread.()java.lang.StringgetName (Any)java.lang.String+ java.lang.String(" exited.")" exited.")
	}
	/** Replaces the uncaught exception handler with one that handles the SecurityException thrown by System.exit and
	* otherwise delegates to the original handler*/
	private def (java.lang.Thread,scala.collection.Set[java.lang.Thread],sbt.ExitCode)UnitreplaceHandler(java.lang.Threadthread: java.lang.ThreadThread, scala.collection.Set[java.lang.Thread]originalThreads: scala.collection.Set[java.lang.Thread]Set[Thread], sbt.ExitCodecode: sbt.ExitCodeExitCode)
	{
		java.lang.Threadthread.(java.lang.Thread.UncaughtExceptionHandler)UnitsetUncaughtExceptionHandler(sbt.TrapExit.ExitHandlernew sbt.TrapExit.ExitHandlerExitHandler(java.lang.Threadthread.()java.lang.Thread.UncaughtExceptionHandlergetUncaughtExceptionHandler, scala.collection.Set[java.lang.Thread]originalThreads, sbt.ExitCodecode))
	}
	/** Returns the exit code of the System.exit that caused the given Exception, or rethrows the exception
	* if its cause was not calling System.exit.*/
	private def (Throwable)IntexitCode(Throwablee: ThrowableThrowable): IntInt =
	{
		Throwablee Intmatch
		{
			Intcase sbt.TrapExitSecurityExceptionx: sbt.TrapExitSecurityExceptionTrapExitSecurityException => sbt.TrapExitSecurityExceptionx.=> IntexitCode
			Intcase _ => 
			{
				val java.lang.Throwablecause = Throwablee.()java.lang.ThrowablegetCause
				Intif(java.lang.Throwablecause (AnyRef)Boolean== Null(null)null)
					Nothingthrow Throwablee
				else
					(Throwable)IntexitCode(java.lang.Throwablecause)
			}
		}
	}
	
	import scala.collection.jcl.Conversions.convertSet
	/** Returns all threads that are not in the 'system' thread group and are not the AWT implementation
	* thread (AWT-XAWT, AWT-Windows, ...)*/
	private def => scala.collection.Set[java.lang.Thread]allThreads: scala.collection.Set[java.lang.Thread]Set[Thread] =
	{
		val <refinement> extends java.lang.Object with scala.collection.jcl.SetWrapper[java.lang.Thread]threads = (java.util.Set[java.lang.Thread])java.lang.Object with scala.collection.jcl.SetWrapper[java.lang.Thread]convertSet(object java.lang.ThreadThread.()java.util.Map[java.lang.Thread,Array[java.lang.StackTraceElement]]getAllStackTraces.()java.util.Set[java.lang.Thread]keySet)
		for(((java.lang.Thread) => Unit)Unitthread <- <refinement> extends java.lang.Object with scala.collection.jcl.SetWrapper[java.lang.Thread]threads.=> List[java.lang.Thread]toList if (java.lang.Thread)BooleanisSystemThread(java.lang.Threadthread))
			<refinement> extends java.lang.Object with scala.collection.jcl.SetWrapper[java.lang.Thread]threads (java.lang.Thread)Unit-= java.lang.Threadthread
		<refinement> extends java.lang.Object with scala.collection.jcl.SetWrapper[java.lang.Thread]threads
	}
	/** Returns true if the given thread is in the 'system' thread group and is an AWT thread other than
	* AWT-EventQueue or AWT-Shutdown.*/
	private def (java.lang.Thread)BooleanisSystemThread(java.lang.Threadt: java.lang.ThreadThread) =
	{
		val java.lang.Stringname = java.lang.Threadt.()java.lang.StringgetName
		Booleanif(java.lang.Stringname.(java.lang.String)BooleanstartsWith(java.lang.String("AWT-")"AWT-"))
			=> Boolean!(java.lang.Stringname.(java.lang.String)BooleanstartsWith(java.lang.String("AWT-EventQueue")"AWT-EventQueue") (Boolean)Boolean|| java.lang.Stringname.(java.lang.String)BooleanstartsWith(java.lang.String("AWT-Shutdown")"AWT-Shutdown"))
		else
		{
			val java.lang.ThreadGroupgroup = java.lang.Threadt.()java.lang.ThreadGroupgetThreadGroup
			(java.lang.ThreadGroupgroup (AnyRef)Boolean!= Null(null)null) (Boolean)Boolean&& (java.lang.ThreadGroupgroup.()java.lang.StringgetName (AnyRef)Boolean== java.lang.String("system")"system")
		}
	}
	/** Calls the provided function for each thread in the system as provided by the 
	* allThreads function except those in ignoreThreads.*/
	private def (scala.collection.Set[java.lang.Thread],(java.lang.Thread) => Unit)UnitprocessThreads(scala.collection.Set[java.lang.Thread]ignoreThreads: scala.collection.Set[java.lang.Thread]Set[Thread], (java.lang.Thread) => Unitprocess: Thread => Unit)
	{
		=> scala.collection.Set[java.lang.Thread]allThreads.((java.lang.Thread) => Boolean)Iterable[java.lang.Thread]filter(java.lang.Threadthread => => Boolean!scala.collection.Set[java.lang.Thread]ignoreThreads.(java.lang.Thread)Booleancontains(java.lang.Threadthread)).((java.lang.Thread) => Unit)Unitforeach((java.lang.Thread) => Unitprocess)
	}
	/** Handles System.exit by disposing all frames and calling interrupt on all user threads */
	private def (scala.collection.Set[java.lang.Thread])UnitstopAll(scala.collection.Set[java.lang.Thread]originalThreads: scala.collection.Set[java.lang.Thread]Set[Thread])
	{
		val Array[java.awt.Frame]allFrames = java.awt.object java.awt.FrameFrame.()Array[java.awt.Frame]getFrames
		Unitif(Array[java.awt.Frame]allFrames.=> Intlength (Int)Boolean> Int(0)0)
		{
			Array[java.awt.Frame]allFrames.((java.awt.Frame) => Unit)Unitforeach(java.awt.Frame_.()Unitdispose) // dispose all top-level windows, which will cause the AWT-EventQueue-* threads to exit
			object java.lang.ThreadThread.(Long)Unitsleep(Long(2000L)2000) // AWT Thread doesn't exit immediately, so wait to interrupt it
		}
		// interrupt all threads that appear to have been started by the user
		(scala.collection.Set[java.lang.Thread],(java.lang.Thread) => Unit)UnitprocessThreads(scala.collection.Set[java.lang.Thread]originalThreads, java.lang.Threadthread => Unitif(=> Boolean!java.lang.Threadthread.()java.lang.StringgetName.(java.lang.String)BooleanstartsWith(java.lang.String("AWT-")"AWT-")) java.lang.Threadthread.()Unitinterrupt)
	}
	/** An uncaught exception handler that delegates to the original uncaught exception handler except when
	* the cause was a call to System.exit (which generated a SecurityException)*/
	class class ExitHandler extends java.lang.Object with java.lang.Thread.UncaughtExceptionHandler with ScalaObjectExitHandler(java.lang.Thread.UncaughtExceptionHandleroriginalHandler: Thread.java.lang.Thread.UncaughtExceptionHandlerUncaughtExceptionHandler, scala.collection.Set[java.lang.Thread]originalThreads: scala.collection.Set[java.lang.Thread]Set[Thread], sbt.ExitCodecodeHolder: sbt.ExitCodeExitCode) extends Thread.java.lang.Thread.UncaughtExceptionHandlerUncaughtExceptionHandler
	{
		def (java.lang.Thread,Throwable)UnituncaughtException(java.lang.Threadt: java.lang.ThreadThread, Throwablee: ThrowableThrowable)
		{
			try
			{
				sbt.ExitCodecodeHolder.(Int)Unitset((Throwable)IntexitCode(Throwablee)) // will rethrow e if it was not because of a call to System.exit
				(scala.collection.Set[java.lang.Thread])UnitstopAll(scala.collection.Set[java.lang.Thread]originalThreads)
			}
			catch
			{
				Unitcase _ => java.lang.Thread.UncaughtExceptionHandleroriginalHandler.(java.lang.Thread,java.lang.Throwable)UnituncaughtException(java.lang.Threadt, Throwablee)
			}
		}
	}
}
private class class ExitCode extends java.lang.Object with NotNull with ScalaObjectExitCode extends NotNullNotNull
{
	private var Option[Int]code: Option[Int]Option[Int] = object NoneNone
	def (Int)Unitset(Intc: IntInt)
	{
		(Unit)Unitsynchronized
		{
			=> Option[Int]code Unitmatch
			{
				Unitcase Some(Intexisting) => Unit()
				Unitcase object NoneNone => (Option[Int])Unitcode = (Int)Some[Int]Some(Intc)
			}
		}
	}
	def => Option[Int]value: Option[Int]Option[Int] = (Option[Int])Option[Int]synchronized { => Option[Int]code }
}
///////  These two classes are based on similar classes in Nailgun
/** A custom SecurityManager to disallow System.exit. */
private class class TrapExitSecurityManager extends java.lang.SecurityManager with ScalaObjectTrapExitSecurityManager(java.lang.SecurityManagerdelegateManager: java.lang.SecurityManagerSecurityManager) extends java.lang.SecurityManagerSecurityManager
{
	import java.security.Permission
	override def (Int)UnitcheckExit(Intstatus: IntInt)
	{
		val Array[java.lang.StackTraceElement]stack = object java.lang.ThreadThread.()java.lang.ThreadcurrentThread.()Array[java.lang.StackTraceElement]getStackTrace
		Unitif(Array[java.lang.StackTraceElement]stack (AnyRef)Boolean== Null(null)null (Boolean)Boolean|| Array[java.lang.StackTraceElement]stack.((java.lang.StackTraceElement) => Boolean)Booleanexists((java.lang.StackTraceElement)BooleanisRealExit))
			Nothingthrow sbt.TrapExitSecurityExceptionnew sbt.TrapExitSecurityExceptionTrapExitSecurityException(Intstatus)
	}
	/** This ensures that only actual calls to exit are trapped and not just calls to check if exit is allowed.*/
	private def (java.lang.StackTraceElement)BooleanisRealExit(java.lang.StackTraceElementelement: java.lang.StackTraceElementStackTraceElement): BooleanBoolean =
		java.lang.StackTraceElementelement.()java.lang.StringgetClassName (AnyRef)Boolean== java.lang.String("java.lang.Runtime")"java.lang.Runtime" (Boolean)Boolean&& java.lang.StackTraceElementelement.()java.lang.StringgetMethodName (AnyRef)Boolean== java.lang.String("exit")"exit"
	override def (java.security.Permission)UnitcheckPermission(java.security.Permissionperm: java.security.PermissionPermission)
	{
		Unitif(java.lang.SecurityManagerdelegateManager (AnyRef)Boolean!= Null(null)null)
			java.lang.SecurityManagerdelegateManager.(java.security.Permission)UnitcheckPermission(java.security.Permissionperm)
	}
	override def (java.security.Permission,AnyRef)UnitcheckPermission(java.security.Permissionperm: java.security.PermissionPermission, AnyRefcontext: AnyRefAnyRef)
	{
		Unitif(java.lang.SecurityManagerdelegateManager (AnyRef)Boolean!= Null(null)null)
			java.lang.SecurityManagerdelegateManager.(java.security.Permission,Any)UnitcheckPermission(java.security.Permissionperm, AnyRefcontext)
	}
}
/** A custom SecurityException that tries not to be caught.*/
private class class TrapExitSecurityException extends java.lang.SecurityException with ScalaObjectTrapExitSecurityException(val IntexitCode: IntInt) extends java.lang.SecurityExceptionSecurityException
{
	private var BooleanaccessAllowed = Boolean(false)false
	def => UnitallowAccess
	{
		(Boolean)UnitaccessAllowed = Boolean(true)true
	}
	override def ()UnitprintStackTrace = (=> Unit)UnitifAccessAllowed(()Unitsuper.()UnitprintStackTrace)
	override def ()java.lang.StringtoString = (=> java.lang.String)java.lang.StringifAccessAllowed(()java.lang.Stringsuper.()java.lang.StringtoString)
	override def ()java.lang.ThrowablegetCause = (=> java.lang.Throwable)java.lang.ThrowableifAccessAllowed(()java.lang.Throwablesuper.()java.lang.ThrowablegetCause)
	override def ()java.lang.StringgetMessage = (=> java.lang.String)java.lang.StringifAccessAllowed(()java.lang.Stringsuper.()java.lang.StringgetMessage)
	override def ()java.lang.ThrowablefillInStackTrace = (=> java.lang.Throwable)java.lang.ThrowableifAccessAllowed(()java.lang.Throwablesuper.()java.lang.ThrowablefillInStackTrace)
	override def ()java.lang.StringgetLocalizedMessage = (=> java.lang.String)java.lang.StringifAccessAllowed(()java.lang.Stringsuper.()java.lang.StringgetLocalizedMessage)
	private def [T](=> T)TifAccessAllowed[>: Nothing <: AnyT](=> Tf: => T): TT =
	{
		Tif(=> BooleanaccessAllowed)
			=> Tf
		else
			Nothingthrow sbt.TrapExitSecurityExceptionthis
	}
}