xsbt.api.Discovery.scala Maven / Gradle / Ivy
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package xsbt.api
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 isConcrete(c.modifiers) =>
if(isPublic(c))
discover(c)
else if(isModule(c) && hasMainMethod(c)) // jvm does not require a main class to be public
new Discovered(Set.empty, Set.empty, true, true)
else
Discovered.empty
case _ => Discovered.empty
}
def discover(c: ClassLike): Discovered =
{
val onClass = Discovery.findAnnotations(c.annotations, annotations)
val onDefs = Discovery.defAnnotations(c.structure, annotations) ++ c.savedAnnotations.filter(annotations)
val module = isModule(c)
new Discovered( bases(c.name, c.structure.parents), onClass ++ onDefs, module && hasMainMethod(c), module )
}
def bases(own: String, c: Seq[Type]): Set[String] =
(own +: c.flatMap(simpleName)).filter(baseClasses).toSet
}
object Discovery
{
def apply(subclasses: Set[String], annotations: Set[String])(definitions: Seq[Definition]): Seq[(Definition, Discovered)] =
{
val d = new Discovery(subclasses, annotations)
d(definitions)
}
def applications(definitions: Seq[Definition]): Seq[(Definition, Discovered)] =
apply(Set.empty, Set.empty)( definitions )
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 )
def isConcrete(a: Definition): Boolean = isConcrete(a.modifiers)
def isConcrete(m: Modifiers) = !m.isAbstract
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 {
case d: Def => d.name == "main" && isPublic(d) && isConcrete(d) && isUnit(d.returnType) && isStringArray(d.valueParameters)
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 || p.modifier == ParameterModifier.Repeated) && 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")
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy