aboutsummaryrefslogtreecommitdiff
path: root/libraries/reflect/reflect.scala
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2017-03-20 22:09:38 -0400
committerChristopher Vogt <oss.nsp@cvogt.org>2017-03-27 19:56:13 -0400
commitbba2abe7ee38b8903822a07578c46466923d13ed (patch)
treea357fb8def6f58a9ea9a37411f3f5640dcb525fe /libraries/reflect/reflect.scala
parentd2f8cade709b7d55a93e18592b6e38247d648ca9 (diff)
downloadcbt-bba2abe7ee38b8903822a07578c46466923d13ed.tar.gz
cbt-bba2abe7ee38b8903822a07578c46466923d13ed.tar.bz2
cbt-bba2abe7ee38b8903822a07578c46466923d13ed.zip
start modularizing cbt into libraries
this extracts certain parts of cbt into stand-alone libraries, which can be published to maven and used outside of cbt. This also adds scalariform for these parts of the code. This slows down cbt’s own build a lot because of the number of projects involved! So we’ll follow this by a bunch of performance tweak commits.
Diffstat (limited to 'libraries/reflect/reflect.scala')
-rw-r--r--libraries/reflect/reflect.scala177
1 files changed, 177 insertions, 0 deletions
diff --git a/libraries/reflect/reflect.scala b/libraries/reflect/reflect.scala
new file mode 100644
index 0000000..c18d926
--- /dev/null
+++ b/libraries/reflect/reflect.scala
@@ -0,0 +1,177 @@
+package cbt.reflect
+
+import java.io.File
+import java.lang.reflect.{ Constructor, Method, InvocationTargetException, Modifier }
+
+import scala.reflect.ClassTag
+
+import cbt.ExitCode
+import cbt.file._
+
+object `package` extends Module {
+ implicit class CbtClassOps( val c: Class[_] ) extends AnyVal with ops.CbtClassOps
+ implicit class CbtConstructorOps( val c: Constructor[_] ) extends AnyVal with ops.CbtConstructorOps
+ implicit class CbtMethodOps( val m: Method ) extends AnyVal with ops.CbtMethodOps
+}
+
+package ops {
+ trait CbtClassOps extends Any {
+ def c: Class[_]
+ def name = c.getName
+ def method( name: String, parameterTypes: Class[_]* ) = c.getMethod( name, parameterTypes: _* )
+ def methods = c.getMethods
+ def modifiers = c.getModifiers
+ def constructors = c.getConstructors
+ def declaredMethods = c.getDeclaredMethods
+ def isInterface = Modifier.isInterface( c.getModifiers )
+ def isAbstract = Modifier.isAbstract( c.getModifiers )
+ def isPrivate = Modifier.isPrivate( c.getModifiers )
+ def isProtected = Modifier.isProtected( c.getModifiers )
+ def isPublic = Modifier.isPublic( c.getModifiers )
+ def isFinal = Modifier.isFinal( c.getModifiers )
+ }
+ trait CbtConstructorOps extends Any {
+ def c: Constructor[_]
+ def parameterTypes = c.getParameterTypes
+ }
+ trait CbtMethodOps extends Any {
+ def m: Method
+ def name = m.getName
+ def declaringClass = m.getDeclaringClass
+ def modifiers = m.getModifiers
+ def parameters = m.getParameters
+ def parameterTypes = m.getParameterTypes
+ def returnType = m.getReturnType
+
+ def isAbstract = Modifier.isAbstract( m.getModifiers )
+ def isFinal = Modifier.isFinal( m.getModifiers )
+ def isNative = Modifier.isNative( m.getModifiers )
+ def isPrivate = Modifier.isPrivate( m.getModifiers )
+ def isProtected = Modifier.isProtected( m.getModifiers )
+ def isPublic = Modifier.isPublic( m.getModifiers )
+ def isStatic = Modifier.isStatic( m.getModifiers )
+ def isStrict = Modifier.isStrict( m.getModifiers )
+ def isSynchronized = Modifier.isSynchronized( m.getModifiers )
+ def isTransient = Modifier.isTransient( m.getModifiers )
+ def isVolatile = Modifier.isVolatile( m.getModifiers )
+ }
+}
+trait Module {
+ def runMain( cls: Class[_], args: Seq[String] ): ExitCode =
+ discoverStaticExitMethodForced[Array[String]]( cls, "main" ).apply( args.to )
+
+ def discoverMain( cls: Class[_] ): Option[StaticMethod[Seq[String], ExitCode]] = {
+ discoverStaticExitMethod[Array[String]]( cls, "main" )
+ .map( f =>
+ f.copy(
+ function = ( arg: Seq[String] ) => f.function( arg.to )
+ ) )
+ }
+
+ /** ignoreMissingClasses allows ignoring other classes root directories which are subdirectories of this one */
+ def iterateClasses(
+ classesRootDirectory: File,
+ classLoader: ClassLoader,
+ ignoreMissingClasses: Boolean
+ ): Seq[Class[_]] =
+ iterateClassNames( classesRootDirectory )
+ .map { name =>
+ try {
+ classLoader.loadClass( name )
+ } catch {
+ case e: ClassNotFoundException if ignoreMissingClasses => null
+ case e: NoClassDefFoundError if ignoreMissingClasses => null
+ }
+ }
+ .filterNot( ignoreMissingClasses && _ == null )
+
+ /** Given a directory corresponding to the root package, iterate
+ * the names of all classes derived from the class files found
+ */
+ def iterateClassNames( classesRootDirectory: File ): Seq[String] =
+ classesRootDirectory.listRecursive
+ .filter( _.isFile )
+ .map( _.getPath )
+ .collect {
+ // no $ to avoid inner classes
+ case path if !path.contains( "$" ) && path.endsWith( ".class" ) =>
+ path
+ .stripSuffix( ".class" )
+ .stripPrefix( classesRootDirectory.getPath )
+ .stripPrefix( File.separator ) // 1 for the slash
+ .replace( File.separator, "." )
+ }
+
+ def discoverStaticExitMethodForced[Arg: ClassTag](
+ cls: Class[_], name: String
+ ): StaticMethod[Arg, ExitCode] = {
+ val f = discoverStaticMethodForced[Arg, Unit]( cls, name )
+ f.copy(
+ function = arg => trapExitCode { f.function( arg ); ExitCode.Success }
+ )
+ }
+
+ def discoverStaticMethodForced[Arg, Result](
+ cls: Class[_], name: String
+ )(
+ implicit
+ Result: ClassTag[Result], Arg: ClassTag[Arg]
+ ): StaticMethod[Arg, Result] = {
+ val m = cls.method( name, Arg.runtimeClass )
+ assert( Result.runtimeClass.isAssignableFrom( m.returnType ) )
+ typeStaticMethod( m )
+ }
+
+ def discoverStaticExitMethod[Arg: ClassTag](
+ cls: Class[_], name: String
+ ): Option[StaticMethod[Arg, ExitCode]] =
+ discoverStaticMethod[Arg, Unit]( cls, name ).map( f =>
+ f.copy(
+ function = arg => trapExitCode { f.function( arg ); ExitCode.Success }
+ ) )
+
+ def discoverStaticMethod[Arg, Result](
+ cls: Class[_], name: String
+ )(
+ implicit
+ Result: ClassTag[Result], Arg: ClassTag[Arg]
+ ): Option[StaticMethod[Arg, Result]] = {
+ Some( cls )
+ .filterNot( _.isAbstract )
+ .filterNot( _.isInterface )
+ .flatMap( _
+ .getMethods
+ .find( m =>
+ !m.isAbstract
+ && m.isPublic
+ && m.name == name
+ && m.parameterTypes.toList == List( Arg.runtimeClass )
+ && Result.runtimeClass.isAssignableFrom( m.returnType ) ) )
+ .map( typeStaticMethod )
+ }
+
+ def typeStaticMethod[Arg, Result]( method: Method ): StaticMethod[Arg, Result] = {
+ val m = method
+ val instance =
+ if ( m.isStatic ) null
+ else m.declaringClass.newInstance // Dottydoc needs this. It's main method is not static.
+ StaticMethod(
+ arg => m.invoke( instance, arg.asInstanceOf[AnyRef] ).asInstanceOf[Result],
+ m.getClass.name.stripSuffix( "$" ) ++ "." ++ m.name ++ "( "
+ ++ m.parameters.map( _.getType.name ).mkString( ", " )
+ ++ " )"
+ )
+ }
+
+ def trapExitCodeOrValue[T]( result: => T, i: Int = 5 ): Either[ExitCode, T] = {
+ TrapSystemExit.run(
+ new TrapSystemExit[Either[ExitCode, T]] {
+ def run = Right( result )
+ def wrap( exitCode: Int ) = Left( new ExitCode( exitCode ) )
+ }
+ )
+ }
+
+ def trapExitCode( code: => ExitCode ): ExitCode =
+ trapExitCodeOrValue( code ).merge
+}