2010-07-02 12:57:03 +02:00
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
2011-10-06 00:09:27 +02:00
package xsbt.api
2010-07-02 12:57:03 +02:00
import xsbti.api. { Path => APath , _ }
import Discovery._
class Discovery ( baseClasses : Set [ String ] , annotations : Set [ String ] )
{
def apply ( s : Seq [ Definition ] ) : Seq [ ( Definition , Discovered ) ] =
s . map { d => ( d , apply ( d ) ) }
def apply ( d : Definition ) : Discovered =
d match
{
case c : ClassLike if isPublic ( c ) && isConcrete ( c . modifiers ) => discover ( c )
case _ => Discovered . empty
}
def discover ( c : ClassLike ) : Discovered =
{
2011-10-20 04:23:47 +02:00
val onClass = Discovery . findAnnotations ( c . annotations , annotations )
val onDefs = Discovery . defAnnotations ( c . structure , annotations ) ++ c . savedAnnotations . filter ( annotations )
2010-07-02 12:57:03 +02:00
val module = isModule ( c )
2010-10-06 14:24:13 +02:00
new Discovered ( bases ( c . name , c . structure . parents ) , onClass ++ onDefs , module && hasMainMethod ( c ) , module )
2010-07-02 12:57:03 +02:00
}
2010-10-06 14:24:13 +02:00
def bases ( own : String , c : Seq [ Type ] ) : Set [ String ] =
( own +: c . flatMap ( simpleName ) ) . filter ( baseClasses ) . toSet
2011-10-20 04:23:47 +02:00
2010-07-02 12:57:03 +02:00
}
object Discovery
{
2010-09-28 00:40:57 +02:00
def apply ( subclasses : Set [ String ] , annotations : Set [ String ] ) ( definitions : Seq [ Definition ] ) : Seq [ ( Definition , Discovered ) ] =
{
val d = new Discovery ( subclasses , annotations )
d ( definitions )
}
2010-10-06 14:24:13 +02:00
def applications ( definitions : Seq [ Definition ] ) : Seq [ ( Definition , Discovered ) ] =
apply ( Set . empty , Set . empty ) ( definitions )
2010-09-28 00:40:57 +02:00
2011-10-20 04:23:47 +02:00
def findAnnotations ( as : Seq [ Annotation ] , pred : String => Boolean ) : Set [ String ] =
as . flatMap { a => simpleName ( a . base ) . filter ( pred ) } . toSet
def defAnnotations ( s : Structure , pred : String => Boolean ) : Set [ String ] =
defAnnotations ( s . declared , pred ) ++ defAnnotations ( s . inherited , pred )
def defAnnotations ( defs : Seq [ Definition ] , pred : String => Boolean ) : Set [ String ] =
findAnnotations ( defs . flatMap { case d : Def if isPublic ( d ) => d . annotations . toSeq ; case _ => Nil } , pred )
2010-07-02 12:57:03 +02:00
def isConcrete ( a : Definition ) : Boolean = isConcrete ( a . modifiers )
2010-10-23 03:55:16 +02:00
def isConcrete ( m : Modifiers ) = ! m . isAbstract
2010-07-02 12:57:03 +02:00
def isPublic ( a : Definition ) : Boolean = isPublic ( a . access )
def isPublic ( a : Access ) : Boolean = a . isInstanceOf [ Public ]
def isModule ( c : ClassLike ) = c . definitionType == DefinitionType . Module
def hasMainMethod ( c : ClassLike ) : Boolean =
hasMainMethod ( c . structure . declared ) || hasMainMethod ( c . structure . inherited )
def hasMainMethod ( defs : Seq [ Definition ] ) : Boolean =
defs . exists ( isMainMethod )
def isMainMethod ( d : Definition ) : Boolean =
d match {
2010-10-23 03:55:16 +02:00
case d : Def => d . name == "main" && isPublic ( d ) && isConcrete ( d ) && isUnit ( d . returnType ) && isStringArray ( d . valueParameters )
2010-07-02 12:57:03 +02:00
case _ => false
}
def isStringArray ( vp : IndexedSeq [ ParameterList ] ) : Boolean = vp . length == 1 && isStringArray ( vp ( 0 ) . parameters )
def isStringArray ( params : Seq [ MethodParameter ] ) : Boolean = params . length == 1 && isStringArray ( params ( 0 ) )
def isStringArray ( p : MethodParameter ) : Boolean = p . modifier == ParameterModifier . Plain && isStringArray ( p . tpe )
def isStringArray ( t : Type ) : Boolean = isParameterized ( t , "scala.Array" , "java.lang.String" ) // doesn't handle scala.this#Predef#String, should API phase dealias?
def isParameterized ( t : Type , base : String , args : String * ) : Boolean = t match {
case p : Parameterized =>
named ( p . baseType , base ) && p . typeArguments . length == args . length && p . typeArguments . flatMap ( simpleName ) . sameElements ( args )
case _ => false
}
def named ( t : Type , nme : String ) = simpleName ( t ) == Some ( nme )
def simpleName ( t : Type ) : Option [ String ] = t match {
case a : Annotated => simpleName ( a . baseType )
case sing : Singleton => None
case p : Projection =>
p . prefix match {
case s : Singleton => pathName ( s . path , p . id )
case e : EmptyType => Some ( p . id )
case _ => None
}
case _ => None
}
def pathName ( p : APath , id : String ) : Option [ String ] =
{
val cs = p . components
cs . last match
{
case _ : This =>
val ids = cs . init . collect { case i : Id => i . id }
if ( ids . length == cs . length - 1 ) Some ( ( ids ++ Seq ( id ) ) . mkString ( "." ) ) else None
case _ => None
}
}
def isUnit ( t : Type ) : Boolean = named ( t , "scala.Unit" )
}