Skip to content

Commit

Permalink
Make attribute traits inheritable (#280)
Browse files Browse the repository at this point in the history
* Make attribute traits inheritable

* Removed debug code
  • Loading branch information
severn-everett authored Jun 25, 2024
1 parent d12685e commit b4a6eae
Show file tree
Hide file tree
Showing 17 changed files with 1,020 additions and 168 deletions.
8 changes: 3 additions & 5 deletions buildSrc/src/main/kotlin/kotlinx/html/generate/attributes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,12 @@ fun Appendable.attributeProperty(
}

fun Appendable.facade(repository: Repository, facade: AttributeFacade) {
val facadeName = facade.name.capitalize() + "Facade"

clazz(Clazz(facadeName, isInterface = true, parents = listOf("Tag"))) {
clazz(Clazz(facade.className, isInterface = true, parents = facade.parents)) {
}

facade.attributes.filter { !isAttributeExcluded(it.name) }.forEach { attribute ->
facade.declaredAttributes.filter { !isAttributeExcluded(it.name) }.forEach { attribute ->
if (attribute.name.isLowerCase() || attribute.name.lowercase() !in facade.attributeNames) {
attributeProperty(repository, attribute, receiver = facadeName, indent = 0)
attributeProperty(repository, attribute, receiver = facade.className, indent = 0)
}
}
}
Expand Down
9 changes: 3 additions & 6 deletions buildSrc/src/main/kotlin/kotlinx/html/generate/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fun generate(pkg: String, todir: String, jsdir: String, wasmJsDir: String) {
}

repository.attributeFacades.values.forEach { facade ->
facade.attributes.filter { it.enumValues.isNotEmpty() }.filter { !isAttributeExcluded(it.name) }
facade.declaredAttributes.filter { it.enumValues.isNotEmpty() }.filter { !isAttributeExcluded(it.name) }
.forEach { attribute ->
genEnumAttribute(attribute)
}
Expand Down Expand Up @@ -307,17 +307,14 @@ private fun generateConsumerTags(
}

private fun generateEventAttrs(repository: Repository, file: String, pkg: String) {
val isEventAttribute = { attributeName: String ->
attributeName.startsWith("on")
}
val isEventAttribute = { attributeName: String -> attributeName.startsWith("on") }
val properties = sequence {
repository
.attributeFacades
.filter { facade -> facade.value.attributeNames.any(isEventAttribute) }
.forEach { facade ->
facade.value.attributes.filter { it.name.startsWith("on") }.forEach {
val parentName = facade.value.name.capitalize() + "Facade"
val parent = ClassName("kotlinx.html", parentName)
val parent = ClassName("kotlinx.html", facade.value.className)

yield(eventProperty(parent, it, shouldUnsafeCast = false))
}
Expand Down
10 changes: 9 additions & 1 deletion buildSrc/src/main/kotlin/kotlinx/html/generate/model.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@ class Repository {
var unionsByGroups: Map<String, List<GroupUnion>> = emptyMap()
}

data class AttributeFacade(val name: String, val attributes: List<AttributeInfo>, val required: Set<String>) {
data class AttributeFacade(
val name: String,
val declaredAttributes: List<AttributeInfo>,
val required: Set<String>,
val inheritedFacades: List<AttributeFacade>,
) {
val className = name.capitalize() + "Facade"
val attributes: List<AttributeInfo> = declaredAttributes + inheritedFacades.flatMap { it.attributes }
val parents = if (inheritedFacades.isNotEmpty()) inheritedFacades.map { it.className } else listOf("Tag")
val attributeNames = attributes.map { it.name }.toSet()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import java.util.*

fun generateParentInterfaces(repository: Repository, todir: String, packg: String) {
val allParentIfaces = repository.tags.values.filterIgnored().map { tag ->
val parentAttributeIfaces = tag.attributeGroups.map { it.name.humanize().capitalize() + "Facade" }
val parentAttributeIfaces = tag.attributeGroups.map { it.className }
val parentElementIfaces = tag.tagGroupNames.map { it.humanize().capitalize() }
val sum = parentAttributeIfaces + parentElementIfaces

Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/kotlinx/html/generate/tagsgen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ private const val BLOCK_LAMBDA = "block"
private const val CONTENT_LAMBDA = "{+content}"

fun Appendable.tagClass(repository: Repository, tag: TagInfo, excludeAttributes: Set<String>) {
val parentAttributeIfaces = tag.attributeGroups.map { it.name.capitalize() + "Facade" }
val parentAttributeIfaces = tag.attributeGroups.map { it.className }
val parentElementIfaces = tag.tagGroupNames.map { it.humanize().capitalize() }
val allParentIfaces = parentAttributeIfaces + parentElementIfaces
val betterParentIfaces = humanizeJoin(allParentIfaces)
Expand Down
74 changes: 59 additions & 15 deletions buildSrc/src/main/kotlin/kotlinx/html/generate/xsdparser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ fun handleAttributeDeclaration(prefix: String, attributeDeclaration: XSAttribute

if (type.isUnion) {
val enumEntries = type.asUnion()
.filter { it.isRestriction }
.map { it.asRestriction() }
.flatMap { it.declaredFacets ?: emptyList() }
.filter { it.name == "enumeration" }
.map { it.value.value }
.asSequence()
.filter { it.isRestriction }
.map { it.asRestriction() }
.flatMap { it.declaredFacets ?: emptyList() }
.filter { it.name == "enumeration" }
.map { it.value.value }
.toList()

return AttributeInfo(name, AttributeType.STRING, enumValues = enumEntries.toAttributeValues(), enumTypeName = prefix.capitalize() + name.humanize().capitalize())
} else if (type.isPrimitive || type.name in setOf<String?>("integer", "string", "boolean", "decimal")) {
Expand Down Expand Up @@ -74,13 +76,36 @@ fun AttributeInfo.handleSpecialType(tagName: String = ""): AttributeInfo = speci
this.copy(type = type)
} ?: this

private fun parseAttributeFacade(repository: Repository, attributeGroup: XSAttGroupDecl): AttributeFacade {
val requiredNames = HashSet<String>()
val facadeAttributes = attributeGroup.declaredAttributeUses.map { attributeUse ->
val attributeDeclaration = attributeUse.decl
if (attributeUse.isRequired) {
requiredNames.add(attributeDeclaration.name)
}

handleAttributeDeclaration("", attributeDeclaration).handleSpecialType()
}.filter { !it.name.startsWith("On") }.sortedBy { it.name }
val inheritedFacades = attributeGroup.attGroups.map { parseAttributeFacade(repository, it) }
inheritedFacades.forEach { repository.attributeFacades[it.name] = it }
return AttributeFacade(attributeGroup.name, facadeAttributes, requiredNames, inheritedFacades)
}

fun fillRepository(repository: Repository) {
val parser = XSOMParser(SAXParserFactory.newInstance())
parser.parse(SCHEME_URL)
val schema = parser.result.getSchema(HTML_NAMESPACE)

val alreadyIncluded = TreeSet<String> { a, b -> a.compareTo(b, true) }
schema.attGroupDecls.values.sortedByDescending { it.attributeUses.size }.forEach { attributeGroup ->
if (!repository.attributeFacades.containsKey(attributeGroup.name)) {
repository.attributeFacades[attributeGroup.name] = parseAttributeFacade(repository, attributeGroup)
}
/*
repository.attributeFacades.computeIfAbsent(attributeGroup.name) { _ ->
parseAttributeFacade(repository, attributeGroup)
}
*/
/*
val requiredNames = HashSet<String>()
val facadeAttributes = attributeGroup.attributeUses.map { attributeUse ->
val attributeDeclaration = attributeUse.decl
Expand All @@ -96,9 +121,10 @@ fun fillRepository(repository: Repository) {
val name = attributeGroup.name
if (facadeAttributes.isNotEmpty()) {
repository.attributeFacades[name] = AttributeFacade(name, facadeAttributes, requiredNames)
repository.attributeFacades[name] = AttributeFacade(name, facadeAttributes, requiredNames, emptyList())
alreadyIncluded.addAll(facadeAttributes.map { it.name })
}
*/
}

schema.modelGroupDecls.values.forEach { modelGroupDeclaration ->
Expand All @@ -120,16 +146,20 @@ fun fillRepository(repository: Repository) {
val name = elementDeclaration.name
val type = elementDeclaration.type
val suggestedNames = HashSet<String>()
globalSuggestedAttributes.get(name)?.let {
suggestedNames.addAll(it.filter { !it.startsWith("-") })
globalSuggestedAttributes[name]?.let { attributes ->
suggestedNames.addAll(attributes.filter { !it.startsWith("-") })
}
val excluded = globalSuggestedAttributes.get(name)?.filter { it.startsWith("-") }?.map { it.removePrefix("-") } ?: emptyList()
val excluded = globalSuggestedAttributes[name]
?.filter { it.startsWith("-") }
?.map { it.removePrefix("-") }
?.toSet()
?: emptySet()

val tagInfo: TagInfo
if (type.isComplexType) {
val complex = type.asComplexType()
val groupDeclarations = complex.attGroups.flatMap { flattenGroups(it) }.distinct().toList()
val attributeGroups = groupDeclarations.map { repository.attributeFacades[it.name] }.filterNotNull()
val groupDeclarations = complex.attGroups.distinct().sortedBy { it.name }
val attributeGroups = groupDeclarations.mapNotNull { repository.attributeFacades[it.name] }

val attributes = complex.declaredAttributeUses.map {
if (it.isRequired) {
Expand All @@ -146,7 +176,13 @@ fun fillRepository(repository: Repository) {
if (contentTerm != null) {
flattenTerm(contentTerm, children, modelGroupNames)
if (contentTerm.isModelGroup) {
directChildren.addAll(contentTerm.asModelGroup().children.map { it.term }.filter { it.isElementDecl }.map { it.asElementDecl().name })
directChildren.addAll(
contentTerm.asModelGroup()
.children
.map { it.term }
.filter { it.isElementDecl }
.map { it.asElementDecl().name },
)
}
}

Expand All @@ -157,7 +193,15 @@ fun fillRepository(repository: Repository) {
suggestedNames.addAll(attributeGroups.flatMap { it.attributes }.filter { it.name in globalSuggestedAttributeNames }.map { it.name })
suggestedNames.removeAll(excluded)

tagInfo = TagInfo(name, children.toList().sorted(), directChildren, attributeGroups, attributes, suggestedNames, modelGroupNames.sorted().toList())
tagInfo = TagInfo(
name = name,
possibleChildren = children.toList().sorted(),
directChildren = directChildren,
attributeGroups = attributeGroups,
attributes = attributes,
suggestedAttributes = suggestedNames,
tagGroupNames = modelGroupNames.sorted(),
)
} else {
throw UnsupportedOperationException()
}
Expand All @@ -172,4 +216,4 @@ private val xsdToType = mapOf(
"boolean" to AttributeType.BOOLEAN,
"string" to AttributeType.STRING,
"anyURI" to AttributeType.STRING // TODO links
)
)
Loading

0 comments on commit b4a6eae

Please sign in to comment.