Files
kotlin-fork/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateJavaScriptStubs.kt
T
2014-03-19 20:25:10 +04:00

190 lines
7.7 KiB
Kotlin

package generators
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.util.ArrayList
import java.util.TreeMap
import org.w3c.dom.*
import org.w3c.dom.events.*
/**
* This tool generates JavaScript stubs for classes available in the JDK which are already available in the browser environment
* such as the W3C DOM
*/
fun generateDomAPI(file: File): Unit {
val packageName = "org.w3c.dom"
val imports = ""
val classes: List<Class<*>> = arrayListOf(javaClass<Attr>(), javaClass<CDATASection>(),
javaClass<CharacterData>(), javaClass<Comment>(),
javaClass<Document>(), javaClass<DocumentFragment>(), javaClass<DocumentType>(),
javaClass<DOMConfiguration>(),
javaClass<DOMError>(), javaClass<DOMErrorHandler>(),
javaClass<DOMImplementation>(), javaClass<DOMImplementationList>(),
javaClass<DOMLocator>(),
javaClass<DOMStringList>(),
javaClass<Element>(),
javaClass<Entity>(), javaClass<EntityReference>(),
javaClass<NameList>(), javaClass<NamedNodeMap>(), javaClass<Node>(), javaClass<NodeList>(),
javaClass<Notation>(), javaClass<ProcessingInstruction>(),
javaClass<Text>(), javaClass<TypeInfo>(),
javaClass<UserDataHandler>())
writeClassesFile(file, packageName, imports, classes)
}
fun generateDomEventsAPI(file: File): Unit {
val packageName = "org.w3c.dom.events"
val imports = "import org.w3c.dom.*\nimport org.w3c.dom.views.*\n"
val classes: List<Class<*>> = arrayList(javaClass<DocumentEvent>(), javaClass<Event>(),
// TODO see domEventsCode.kt we manually hand craft this for now
// to get the implementation in JS
//
// javaClass<EventListener>(),
javaClass<EventTarget>(),
javaClass<MouseEvent>(), javaClass<MutationEvent>(),
javaClass<UIEvent>())
writeClassesFile(file, packageName, imports, classes)
}
private fun writeClassesFile(file: File, packageName: String, imports: String, classes: List<Class<*>>): Unit {
write(file) {
println("""
package $packageName
$imports
//
// NOTE THIS FILE IS AUTO-GENERATED by the GenerateJavaScriptStubs.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//
import js.noImpl
// Contains stub APIs for the W3C DOM API so we can delegate to the platform DOM instead
""")
fun simpleTypeName(klass: Class<out Any?>?): String {
val answer = klass?.getSimpleName()?.capitalize() ?: "Unit"
return if (answer == "Void") "Unit" else if (answer == "Object") "Any" else answer
}
fun parameterTypeName(klass: Class<out Any?>?): String {
val answer = simpleTypeName(klass)
return if (answer == "String" || answer == "Event" || answer.endsWith("DocumentType")) {
answer + "?"
} else answer
}
for (klass in classes) {
val interfaces = klass.getInterfaces()
val extends = if (interfaces != null && interfaces.size == 1) ": ${interfaces[0]?.getSimpleName()}" else ""
println("native public trait ${klass.getSimpleName()}$extends {")
val methods = klass.getDeclaredMethods().sortBy { it.getName()!! }
if (methods != null) {
// lets figure out the properties versus methods
val validMethods = ArrayList<Method>()
val properties = TreeMap<String, PropertyKind>()
for (method in methods) {
if (method != null) {
val name = method.getName() ?: ""
fun propertyName(): String {
val answer = name.substring(3).decapitalize()
return if (answer == "type") {
"`type`"
} else answer
}
fun propertyType() = simpleTypeName(method.getReturnType())
fun propertyKind(method: Method): PropertyKind {
val propName = propertyName()
return properties.getOrPut(propName) { PropertyKind(propName, "val", method) }
}
val params = method.getParameterTypes()!!
val paramSize = params.size
if (name.size > 3) {
if (name.startsWith("get") && paramSize == 0) {
propertyKind(method).typeName = propertyType()
} else if (name.startsWith("set") && paramSize == 1) {
propertyKind(method).kind = "var"
} else {
validMethods.add(method)
}
} else {
validMethods.add(method)
}
}
}
for (pk in properties.values()!!) {
// some properties might not have a getter defined
// so lets ignore those
val typeName = pk.typeName
if (typeName == null) {
validMethods.add(pk.method)
} else {
println(" public ${pk.kind} ${pk.name}: ${typeName}")
}
}
for (method in validMethods) {
val parameterTypes = method.getParameterTypes()!!
// TODO in java 7 its not easy with reflection to get the parameter argument name...
var counter = 0
val parameters = parameterTypes.map{ "arg${++counter}: ${parameterTypeName(it)}" }.makeString(", ")
val returnType = simpleTypeName(method.getReturnType())
println(" public fun ${method.getName()}($parameters): $returnType = js.noImpl")
}
}
val fields = klass.getDeclaredFields().sortBy { it.getName()!! }
if (fields != null) {
if (fields.isNotEmpty()) {
println("")
println(" public class object {")
for (field in fields) {
if (field != null) {
val modifiers = field.getModifiers()
if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
val fieldType = simpleTypeName(field.getType())
try {
val value = field.get(null)
if (value != null) {
val fieldType = simpleTypeName(field.getType())
println(" public val ${field.getName()}: $fieldType = $value")
}
} catch (e: Exception) {
println("Caught: $e")
e.printStackTrace()
}
}
}
}
println(" }")
}
}
println("}")
println("")
}
}
}
class PropertyKind(val name: String, var kind: String, val method: Method, var typeName: String? = null)
fun write(file: File, block: PrintWriter.() -> Unit): Unit {
println("Generating file: ${file.getCanonicalPath()}")
val writer = PrintWriter(FileWriter(file))
writer.use { writer.block() }
}