1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
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._
import cbt.common_1._
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 )
def show = (
m.name ~ "( "
~ m.parameters.map( _.getType.name ).mkString( ", " )
~ " )"
)
}
}
trait Module {
def getMain( cls: Class[_] ): StaticMethod[Seq[String], ExitCode] = {
val f = findStaticExitMethodOrFail[Array[String]]( cls, "main" )
f.copy(
function = ( args: Seq[String] ) => f.function( args.to )
)
}
def findMain( cls: Class[_] ): Option[StaticMethod[Seq[String], ExitCode]] = {
findStaticExitMethod[Array[String]]( cls, "main" )
.map( f =>
f.copy(
function = ( args: Seq[String] ) => f.function( args.to )
) )
}
/** ignoreMissingClasses allows ignoring other classes root directories which are subdirectories of this one */
def topLevelClasses(
classesRootDirectory: File,
classLoader: ClassLoader,
ignoreMissingClasses: Boolean
): Seq[Class[_]] =
topLevelClassNames( 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, return
* the names of all top-level classes based on the class files found
*/
def topLevelClassNames( 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 findStaticExitMethodOrFail[Arg: ClassTag](
cls: Class[_], name: String
): StaticMethod[Arg, ExitCode] = {
val f = findStaticMethodOrFail[Arg, Unit]( cls, name )
f.copy(
function = arg => trapExitCode { f.function( arg ); ExitCode.Success }
)
}
def findStaticMethodOrFail[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 findStaticExitMethod[Arg: ClassTag](
cls: Class[_], name: String
): Option[StaticMethod[Arg, ExitCode]] =
findStaticMethod[Arg, Unit]( cls, name ).map( f =>
f.copy(
function = arg => trapExitCode { f.function( arg ); ExitCode.Success }
) )
def findStaticMethod[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
)
}
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
}
|