Skip to content

Commit

Permalink
feat(generator-accessor): merge chained accessors into single accesso…
Browse files Browse the repository at this point in the history
…r field (#40)

* feat(generator-accessor): merge chained accessors into single accessor field

* fix(generator-accessor): fix javadoc format

* fix(generator-accessor): squash mappings as well, standalone counter for put mappings and accessor fields

* feat(generator-accessor): kotlin support

* style(generator-accessor): use filter and mutableListOf

* refactor(generator-accessor): extract some common code from Java and Kotlin generation context

* fix(generator-accessor): Kdoc generation for *Mapping classes

* refactor(generator-accessor): improve chain resolving logic

Co-authored-by: Matouš Kučera <[email protected]>

* fix(generator-accessor): fucking whitespace

* refactor: simplify

---------

Co-authored-by: Matouš Kučera <[email protected]>
  • Loading branch information
Misat11 and zlataovce authored May 29, 2024
1 parent bec2d6d commit a4a9238
Show file tree
Hide file tree
Showing 7 changed files with 373 additions and 284 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,36 @@ class AncestryTree<T : MappingTreeView, E : ElementMappingView>(
if (keys.isEmpty()) return null // return early, we're not searching for anything

return if (keys.size == 1) {
val key = keys[0]
val key = keys.first()

find { key in it.lastNames }
} else {
find { keys.all(it.lastNames::contains) }
}
}
}

/**
* Creates a merged node.
*
* Respects existing ordering in case of conflicts - only latest entries are kept, older are discarded.
*
* @param nodes the nodes to merge
* @return the merged node
*/
fun <T : MappingTreeView, E : ElementMappingView> nodeOf(vararg nodes: AncestryTree.Node<T, E>): AncestryTree.Node<T, E> {
require(nodes.isNotEmpty()) {
"No nodes provided"
}

if (nodes.size == 1) return nodes.first()

// create merged node
val lastNode = nodes.last()
return AncestryTree.Node(
lastNode.tree,
nodes.flatMap { it.entries }
.associate { it.key to it.value },
last = lastNode.last
)
}
12 changes: 12 additions & 0 deletions core/src/main/kotlin/me/kcra/takenaka/core/util/collections.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,15 @@ package me.kcra.takenaka.core.util
* @return the entry
*/
fun <K, V> entryOf(key: K, value: V): Map.Entry<K, V> = java.util.AbstractMap.SimpleImmutableEntry(key, value)

/**
* Builds an ordered [Map] from a collection of pairs.
*
* Useful for doing in-place ordering changes before the map is created.
*
* @param action the builder action
* @return the built map
*/
fun <K, V> buildOrderedMap(action: MutableList<Pair<K, V>>.() -> Unit): Map<K, V> {
return mutableListOf<Pair<K, V>>().apply(action).toMap()
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import me.kcra.takenaka.core.mapping.fromInternalName
import me.kcra.takenaka.core.mapping.resolve.impl.craftBukkitNmsVersion
import me.kcra.takenaka.core.mapping.resolve.impl.modifiers
import me.kcra.takenaka.core.mapping.util.dstNamespaceIds
import me.kcra.takenaka.core.util.buildOrderedMap
import me.kcra.takenaka.generator.accessor.AccessorGenerator
import me.kcra.takenaka.generator.accessor.GeneratedClassType
import me.kcra.takenaka.generator.accessor.context.GenerationContext
Expand All @@ -44,21 +45,6 @@ import java.util.*

private val logger = KotlinLogging.logger {}

/**
* A field accessor and ancestry node pair.
*/
typealias ResolvedFieldPair = Pair<FieldAccessor, FieldAncestryNode>

/**
* A constructor accessor and ancestry node pair.
*/
typealias ResolvedConstructorPair = Pair<ConstructorAccessor, MethodAncestryNode>

/**
* A method accessor and ancestry node pair.
*/
typealias ResolvedMethodPair = Pair<MethodAccessor, MethodAncestryNode>

/**
* An implementation base for [GenerationContext].
*
Expand Down Expand Up @@ -142,35 +128,42 @@ abstract class AbstractGenerationContext(
logger.info { "generating accessors for class '${model.internalName}'" }

val fieldTree = ancestryProvider.field<_, _, FieldMappingView>(node)
val fieldAccessors = model.fields.flatMap { resolveFieldChain(fieldTree, it) } +

// fields can't be overloaded, but capitalization matters, which is a problem when making uppercase names from everything
val fieldOverloads = NameOverloads()
val fieldAccessors = model.fields
.map { resolveFieldChain(fieldTree, it, fieldOverloads.generate(it.upperName)) }
.plus(
resolveRequiredFields(fieldTree, model.requiredTypes).map { fieldNode ->
FieldAccessor(getFriendlyName(fieldNode.last.value), getFriendlyDesc(fieldNode.last.value)) to fieldNode
val accessor = FieldAccessor(getFriendlyName(fieldNode.last.value), getFriendlyDesc(fieldNode.last.value))

ResolvedFieldAccessor(accessor, fieldNode, fieldOverloads.generate(accessor.upperName))
}
)

// fields can't be overloaded, but capitalization matters, which is a problem when making uppercase names from everything
val fieldOverloadCount = mutableMapOf<String, Int>()
val fieldOverloads = fieldAccessors.associate { (fieldAccessor, _) ->
fieldAccessor to fieldOverloadCount.compute(fieldAccessor.upperName) { _, i -> i?.inc() ?: 0 }!!
val ctorTree = ancestryProvider.method<_, _, MethodMappingView>(node, constructorMode = ConstructorComputationMode.ONLY)
val ctorAccessors = model.constructors.mapIndexedTo(mutableListOf()) { i, accessor ->
ResolvedMemberAccessor(accessor, resolveConstructor(ctorTree, accessor), i)
}

val ctorTree = ancestryProvider.method<_, _, MethodMappingView>(node, constructorMode = ConstructorComputationMode.ONLY)
val ctorAccessors = model.constructors.map { ResolvedConstructorPair(it, resolveConstructor(ctorTree, it)) } +
resolveRequiredConstructors(ctorTree, model.requiredTypes).map { ctorNode ->
ConstructorAccessor(getFriendlyDesc(ctorNode.last.value)) to ctorNode
}
resolveRequiredConstructors(ctorTree, model.requiredTypes).mapTo(ctorAccessors) { ctorNode ->
ResolvedConstructorAccessor(ConstructorAccessor(getFriendlyDesc(ctorNode.last.value)), ctorNode, ctorAccessors.size)
}

val methodTree = ancestryProvider.method<_, _, MethodMappingView>(node)
val methodAccessors = model.methods.flatMap { resolveMethodChain(methodTree, it) } +

val methodOverloads = NameOverloads()
val methodAccessors = model.methods
.map { resolveMethodChain(methodTree, it, methodOverloads.generate(it.upperName)) }
.plus(
resolveRequiredMethods(methodTree, model.requiredTypes).map { methodNode ->
MethodAccessor(getFriendlyName(methodNode.last.value), getFriendlyDesc(methodNode.last.value)) to methodNode
}
val accessor = MethodAccessor(getFriendlyName(methodNode.last.value), getFriendlyDesc(methodNode.last.value))

val methodOverloadCount = mutableMapOf<String, Int>()
val methodOverloads = methodAccessors.associate { (methodAccessor, _) ->
methodAccessor to methodOverloadCount.compute(methodAccessor.upperName) { _, i -> i?.inc() ?: 0 }!!
}
ResolvedMethodAccessor(accessor, methodNode, methodOverloads.generate(accessor.upperName))
}
)

generateClass(ResolvedClassAccessor(model, node, fieldAccessors, ctorAccessors, methodAccessors, fieldOverloads, methodOverloads))
generateClass(ResolvedClassAccessor(model, node, fieldAccessors, ctorAccessors, methodAccessors))
}

/**
Expand Down Expand Up @@ -228,16 +221,22 @@ abstract class AbstractGenerationContext(
*
* @param tree the ancestry tree
* @param model the model
* @return the nodes
* @param overloadIndex the model overload index
* @return the resolved model
*/
protected open fun resolveFieldChain(tree: FieldAncestryTree, model: FieldAccessor): List<ResolvedFieldPair> = buildList {
var nextNode: FieldAccessor? = model
while (nextNode != null) {
add(ResolvedFieldPair(nextNode, resolveField(tree, nextNode)))
nextNode = nextNode.chain
}
protected open fun resolveFieldChain(tree: FieldAncestryTree, model: FieldAccessor, overloadIndex: Int): ResolvedFieldAccessor {
return ResolvedFieldAccessor(
buildOrderedMap {
var nextNode: FieldAccessor? = model
while (nextNode != null) {
add(nextNode to resolveField(tree, nextNode))
nextNode = nextNode.chain
}

reverse() // last chain member comes first
reverse() // last chain member comes first
},
overloadIndex
)
}

/**
Expand Down Expand Up @@ -318,16 +317,22 @@ abstract class AbstractGenerationContext(
*
* @param tree the ancestry tree
* @param model the model
* @return the nodes
* @param overloadIndex the model overload index
* @return the resolved model
*/
protected open fun resolveMethodChain(tree: MethodAncestryTree, model: MethodAccessor): List<ResolvedMethodPair> = buildList {
var nextNode: MethodAccessor? = model
while (nextNode != null) {
add(ResolvedMethodPair(nextNode, resolveMethod(tree, nextNode)))
nextNode = nextNode.chain
}
protected open fun resolveMethodChain(tree: MethodAncestryTree, model: MethodAccessor, overloadIndex: Int): ResolvedMethodAccessor {
return ResolvedMethodAccessor(
buildOrderedMap {
var nextNode: MethodAccessor? = model
while (nextNode != null) {
add(nextNode to resolveMethod(tree, nextNode))
nextNode = nextNode.chain
}

reverse() // last chain member comes first
reverse() // last chain member comes first
},
overloadIndex
)
}

/**
Expand Down Expand Up @@ -462,3 +467,15 @@ abstract class AbstractGenerationContext(
}
}

/**
* A utility class for managing name overloads.
*/
private class NameOverloads(delegate: MutableMap<String, Int> = mutableMapOf()) : MutableMap<String, Int> by delegate {
/**
* Generates an available index for the supplied name.
*
* @param key the name
* @return the index
*/
fun generate(key: String): Int = compute(key) { _, i -> i?.inc() ?: 0 }!!
}
Loading

0 comments on commit a4a9238

Please sign in to comment.