Skip to content

Commit a4b6cd0

Browse files
author
Shu Zhang
committed
Add ClassWrapper to help handle generic types
1 parent 2a308ff commit a4b6cd0

File tree

2 files changed

+493
-0
lines changed

2 files changed

+493
-0
lines changed
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
package com.wordnik.swagger.core.util
2+
3+
import java.lang.reflect._
4+
import scala.collection.mutable.{ListBuffer, HashMap}
5+
import java.lang.annotation.Annotation
6+
import java.util.IdentityHashMap
7+
import scala.collection.convert.Wrappers.JMapWrapper
8+
import scala.collection.mutable
9+
import sun.reflect.generics.reflectiveObjects.{GenericArrayTypeImpl, ParameterizedTypeImpl}
10+
import scala.util.parsing.combinator.JavaTokenParsers
11+
12+
object ClassWrapper {
13+
def apply(`type`: Type) = new ClassWrapper(`type`)
14+
def forName(name: String): ClassWrapper = forName(name, name => Class.forName(name))
15+
def forName(name: String, classLoader: String => Class[_]): ClassWrapper = {
16+
apply(new NameParser(classLoader).apply(name))
17+
}
18+
19+
implicit def classToClassWrapper(cls: Class[_]) = ClassWrapper(cls)
20+
21+
private class NameParser(classLoader: String => Class[_]) extends JavaTokenParsers {
22+
def `class`: Parser[Class[_]] = ident ~ rep("." ~> ident) ^^ {
23+
case x ~ y => classLoader((x /: y)(_ + "." + _))
24+
}
25+
26+
def typeList: Parser[scala.Array[Type]] = "[" ~> `type` ~ rep("," ~> `type`) <~ "]" ^^ {
27+
case x ~ y => ((ListBuffer(x) /: y)(_ += _)).toArray
28+
}
29+
30+
def `type`: Parser[Type] = array | (`class` ~ (typeList?)) ^^ {
31+
case x ~ None => x
32+
case x ~ Some(y) => ParameterizedTypeImpl.make(x, y.toArray, null)
33+
}
34+
35+
def array: Parser[Type] = "array[" ~> `type` <~ "]" ^^ {
36+
case x: Class[_] => classLoader("[L" + x.getName + ";")
37+
case x: ParameterizedType => GenericArrayTypeImpl.make(x)
38+
}
39+
40+
def apply(input: String): Type = parseAll(`type`, input) match {
41+
case Success(result, _) => result
42+
case _: NoSuccess => null
43+
}
44+
}
45+
}
46+
47+
class ClassWrapper(private val `type`: Type, private val outerScope: ClassWrapper) {
48+
49+
private var clazz: Class[_] = classOf[Object]
50+
private var typeMap: Map[String, ClassWrapper] = Map()
51+
private var superclass: ClassWrapper = _
52+
private var arrayComponent: ClassWrapper = _
53+
private var parsedClasses: mutable.Map[Type, ClassWrapper] = {
54+
if (outerScope == null) {
55+
JMapWrapper(new IdentityHashMap[Type, ClassWrapper]())
56+
} else {
57+
outerScope.parsedClasses
58+
}
59+
}
60+
61+
parseType(`type`)
62+
63+
if (clazz.getGenericSuperclass != null) {
64+
superclass = getOrCreateClassWrapper(clazz.getGenericSuperclass, this)
65+
}
66+
67+
def this(`type`: Type) = this(`type`, null)
68+
69+
def getName: String = {
70+
val clazzName = {
71+
if (isArray) {
72+
"array[" + arrayComponent.getName + "]"
73+
} else {
74+
clazz.getName
75+
}
76+
}
77+
getName(clazzName)
78+
}
79+
80+
def getName(clazzName: String): String = {
81+
val types = clazz.getTypeParameters.map(t => getTypeArgument(t.getName).getName)
82+
clazzName + (if (types.length > 0) types.mkString("[", ",", "]") else "")
83+
}
84+
85+
def getSimpleName: String = {
86+
val clazzName = {
87+
if (isArray) {
88+
"array[" + arrayComponent.getSimpleName + "]"
89+
} else {
90+
clazz.getSimpleName
91+
}
92+
}
93+
getSimpleName(clazzName)
94+
}
95+
96+
def getSimpleName(clazzName: String): String = {
97+
val types = clazz.getTypeParameters.map(t => getTypeArgument(t.getName).getSimpleName)
98+
clazzName + (if (types.length > 0) types.mkString("[", ",", "]") else "")
99+
}
100+
101+
def getSuperclass: ClassWrapper = superclass
102+
103+
def getRawClass: Class[_] = clazz
104+
105+
def getRawType: Type = `type`
106+
107+
def getTypeArgument(arg: String): ClassWrapper = {
108+
typeMap.get(arg) match {
109+
case Some(t) => t
110+
case None => {
111+
if (outerScope != null) {
112+
outerScope.getTypeArgument(arg)
113+
} else {
114+
null
115+
}
116+
}
117+
}
118+
}
119+
120+
def isArray: Boolean = arrayComponent != null
121+
122+
def getArrayComponent: ClassWrapper = arrayComponent
123+
124+
def getMethodReturnType(name: String, parameterTypes: Class[_]*): ClassWrapper = {
125+
val method = clazz.getMethod(name, parameterTypes: _*)
126+
getMethodReturnType(method)
127+
}
128+
129+
def getMethodReturnType(method: Method): ClassWrapper = {
130+
val methodClass = method.getDeclaringClass
131+
132+
var genericClass = this
133+
while (genericClass != null && genericClass.clazz != methodClass) {
134+
genericClass = genericClass.superclass
135+
}
136+
assert(genericClass != null)
137+
138+
getOrCreateClassWrapper(method.getGenericReturnType, genericClass)
139+
}
140+
141+
def getFieldType(name: String): ClassWrapper = {
142+
val field = clazz.getField(name)
143+
getFieldType(field)
144+
}
145+
146+
def getFieldType(field: Field): ClassWrapper = {
147+
val fieldClass = field.getDeclaringClass
148+
149+
var genericClass = this
150+
while (genericClass != null && genericClass.clazz != fieldClass) {
151+
genericClass = genericClass.superclass
152+
}
153+
assert(genericClass != null)
154+
155+
getOrCreateClassWrapper(field.getGenericType, genericClass)
156+
}
157+
158+
// forward to clazz
159+
def newInstance = clazz.newInstance
160+
def getAnnotation[T <: Annotation](cls: Class[T]): T = clazz.getAnnotation(cls)
161+
def getAnnotations: Seq[Annotation] = clazz.getAnnotations()
162+
def isEnum: Boolean = clazz.isEnum
163+
def getEnumConstants: Seq[_] = clazz.getEnumConstants
164+
def getDeclaredMethods: Seq[Method] = clazz.getDeclaredMethods
165+
def getDeclaredFields: Seq[Field] = clazz.getDeclaredFields
166+
def getDeclaredField(name: String): Field = clazz.getDeclaredField(name)
167+
168+
override def toString: String = getName
169+
170+
// private methods
171+
private def parseType(`type`: Type) = {
172+
assert(!parsedClasses.contains(`type`))
173+
parsedClasses += `type` -> this
174+
`type` match {
175+
case t: Class[_] => parseClass(t)
176+
case t: ParameterizedType => parseParameterizedType(t)
177+
case t: GenericArrayType => parseGenericArrayType(t)
178+
case t: TypeVariable[_] => parseTypeVariable(t)
179+
case t: WildcardType => ()
180+
case _ => ()
181+
}
182+
}
183+
184+
private def parseClass(`type`: Class[_]) = {
185+
clazz = `type`
186+
if (clazz.isArray) {
187+
arrayComponent = getOrCreateClassWrapper(clazz.getComponentType)
188+
}
189+
if (clazz.getTypeParameters.size > 0) {
190+
typeMap = clazz.getTypeParameters.map(t => t.getName -> getOrCreateClassWrapper(classOf[Object])).toMap
191+
}
192+
}
193+
194+
private def parseParameterizedType(`type`: ParameterizedType) = {
195+
val declaredTypes = `type`.getRawType.asInstanceOf[Class[_]].getTypeParameters
196+
val actualTypes = `type`.getActualTypeArguments
197+
assert(declaredTypes.length == actualTypes.length)
198+
199+
val results = new HashMap[String, ClassWrapper]
200+
for (i <- 0 until declaredTypes.length) {
201+
results += (declaredTypes(i).getName -> getOrCreateClassWrapper(actualTypes(i), this))
202+
}
203+
204+
clazz = `type`.getRawType.asInstanceOf[Class[_]]
205+
typeMap = results.toMap
206+
}
207+
208+
private def parseGenericArrayType(`type`: GenericArrayType) = {
209+
val componentClass = getOrCreateClassWrapper(`type`.getGenericComponentType, this)
210+
val className: String = "[L" + componentClass.getRawClass.getName + ";"
211+
212+
clazz = Class.forName(className)
213+
arrayComponent = componentClass
214+
}
215+
216+
private def parseTypeVariable(`type`: TypeVariable[_]) = {
217+
val genericDeclaration: GenericDeclaration = `type`.getGenericDeclaration.asInstanceOf[GenericDeclaration]
218+
if (genericDeclaration.isInstanceOf[Class[_]]) {
219+
val t: ClassWrapper = outerScope.getTypeArgument(`type`.getName)
220+
if (t != null) {
221+
clazz = t.clazz
222+
typeMap = t.typeMap
223+
}
224+
}
225+
}
226+
227+
private def getClassName() = {
228+
if (isArray) {
229+
"array[" + arrayComponent.getName + "]"
230+
} else {
231+
clazz.getName
232+
}
233+
}
234+
235+
private def getClassSimpleName() = {
236+
if (isArray) {
237+
"array[" + arrayComponent.getSimpleName + "]"
238+
} else {
239+
clazz.getSimpleName
240+
}
241+
}
242+
243+
private def getOrCreateClassWrapper(`type`: Type, parent: ClassWrapper = null): ClassWrapper = {
244+
parsedClasses.get(`type`) match {
245+
case Some(t: ClassWrapper) => t
246+
case None => new ClassWrapper(`type`, parent)
247+
}
248+
}
249+
250+
}

0 commit comments

Comments
 (0)