* Copyright (c) 2025-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as idl from '@idlizer/core/idl'
import { getSuper, IfStatement, Language, LanguageExpression, LanguageStatement, LanguageWriter,
LayoutNodeRole, LayoutTargetDescription, Method, MethodModifier, MethodSignature, NamedMethodSignature, PeerClass,
PeerLibrary, PeerMethod } from "@idlizer/core";
import { getHookMethod, collectDeclDependencies, collectDeclItself, collectPeers, componentToPeerClass,
findComponentByDeclaration, findComponentByName, groupOverloads, IdlComponentDeclaration, ImportsCollector,
peerGeneratorConfiguration, PrinterResult, collectModifiers,
ModifierInfo,
collectComponents} from "@idlizer/libohos";
import { expandComponentWithSupers, generateAttributeModifierSignature } from './ComponentsPrinter.js';
import { getReferenceTo } from '../knownReferences.js';
import { HandwrittenModule } from '../ArkoalaLayout.js'
function findPeerByComponentDeclaration(library: PeerLibrary, component: IdlComponentDeclaration): PeerClass | undefined {
return collectPeers(library).find(it => it.componentName === component.name)
}
function capitalizeFirstLetter(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}
function getAttributeModifierClassName(method: PeerMethod): string {
return `${capitalizeFirstLetter(method.method.name)}Modifier`
}
function isPrimitiveType(type: idl.IDLType): boolean {
const visitType = (t: idl.IDLType): boolean => {
if (idl.isOptionalType(t)) {
return visitType(t.type);
}
if (idl.isUnionType(t)) {
return t.types.every(visitType);
}
if (idl.isContainerType(t)) {
return t.elementType.every(visitType);
}
return idl.isPrimitiveType(t);
}
return visitType(type);
}
function isOptionalType(type: idl.IDLType): boolean {
const visiteType = (t: idl.IDLType): boolean => {
if (idl.isOptionalType(t)) {
return true;
}
if (idl.isUnionType(t)) {
return t.types.some(visiteType);
}
if (idl.isContainerType(t)) {
return t.elementType.some(visiteType);
}
return idl.isUndefinedType(t);
}
return visiteType(type);
}
interface AttributeType {
peer: PeerClass;
method: PeerMethod;
args: string[];
argTypes: idl.IDLType[];
isOptional: boolean;
overloadIndex: number;
}
enum AttributeUpdaterFlag {
INITIAL = 0,
UPDATE = 1,
RESET = 2,
SKIP = 3
}
class ModifiersFileVisitor {
constructor(
protected readonly library: PeerLibrary,
private readonly modifiers: ModifierInfo[],
) { }
visit(): PrinterResult[] {
const result: PrinterResult[] = [];
this.modifiers.forEach(modifierInfo => {
result.push(...this.printModifiers(modifierInfo))
})
return result;
}
generateAttributeSetParentName(modifier: ModifierInfo): string | undefined {
if (modifier.parent) {
return this.generateAuxiliaryClassName(modifier.parent)
}
}
generateAttributeSetBaseName(modifier: ModifierInfo): string {
return `${this.generateAttributeSetDerivedName(modifier)}Base`
}
generateAttributeSetDerivedName(modifier: ModifierInfo): string {
return modifier.modifier ? modifier.modifier.name : `${modifier.peer.componentName}Modifier`
}
generateAttributeSetName(modifier: ModifierInfo): string {
if (modifier.isTrivial === false) {
return this.generateAttributeSetBaseName(modifier)
}
return this.generateAttributeSetDerivedName(modifier)
}
generateAuxiliaryClassName(modifier: ModifierInfo): string {
return modifier.modifier ?
`${modifier.modifier.name}Functions` :
`${modifier.peer.componentName}ModifierFunctions`
}
generateAddrName(peer: PeerClass) {
return `_${peer.componentName}_addr`
}
generateFilledFlagName(peer: PeerClass) {
return `_${peer.componentName}_flagArray`
}
generateFiledName(attribute: AttributeType, subfix: string = ''): string {
return `_${attribute.peer.componentName}_${attribute.method.method.name}_${attribute.overloadIndex.toString()}_${subfix}value`
}
generateFiledFlag(attribute: AttributeType, index: number, isLocal: boolean = false): string {
if (isLocal) {
return `flagArray[${index}]`
}
return `this.${this.generateFilledFlagName(attribute.peer)}[${index}]`
}
castResetType(writer: LanguageWriter, sig: MethodSignature, index: number): LanguageExpression {
if (!sig.isArgOptional(index)) {
return writer.makeCast(writer.makeUndefined(), sig.args[index])
}
return writer.makeCast(writer.makeUndefined(), idl.createUnionType([sig.args[index], idl.createPrimitiveType('undefined')]))
}
castSetType(attribute: AttributeType, writer: LanguageWriter, sig: MethodSignature, index: number): LanguageExpression {
const hasUndefinedType = (type: idl.IDLType) => {
if (idl.isUnionType(type)) {
return type.types.some(t => idl.isUndefinedType(t))
}
return false;
}
if (sig.isArgOptional(index) && !hasUndefinedType(sig.args[index])) {
return writer.makeCast(writer.makeString(`this.${this.generateFiledName(attribute, index.toString())}`), idl.createUnionType([sig.args[index], idl.createPrimitiveType('undefined')]))
}
return writer.makeCast(writer.makeString(`this.${this.generateFiledName(attribute, index.toString())}`), sig.args[index])
}
recursiveCollect(component: IdlComponentDeclaration, importsCollector: ImportsCollector) {
let [parentRef] = component.attributeDeclaration.inheritance
let parentDecl = this.library.resolveTypeReference(parentRef)
const parentComponent = findComponentByDeclaration(this.library, parentDecl as idl.IDLInterface)!
console.log("recursiveCollect", parentComponent.attributeDeclaration.name, parentComponent.name)
collectDeclDependencies(this.library, parentComponent.attributeDeclaration, importsCollector)
expandComponentWithSupers(this.library, parentComponent.attributeDeclaration).forEach(decl => {
collectDeclItself(this.library, decl, importsCollector)
})
collectDeclItself(this.library, parentComponent.attributeDeclaration, importsCollector)
}
private getLayoutTargetDescription(
modifier: ModifierInfo,
isInterface: boolean,
component: idl.IDLInterface): LayoutTargetDescription {
return {
node: (modifier.modifier !== undefined && modifier.isTrivial) ? modifier.modifier : component,
role: isInterface ? LayoutNodeRole.INTERFACE : LayoutNodeRole.MODIFIER_FUNCTIONS,
hint: 'component.modifier'
}
}
printImports(modifier: ModifierInfo): ImportsCollector {
const peer = modifier.peer
const component = findComponentByName(this.library, peer.componentName)!
const importsCollector = new ImportsCollector
if (this.needCollectParentMethods(modifier)) {
this.recursiveCollect(component, importsCollector)
}
let parentModifier = modifier.parent
while (parentModifier) {
const parentComponent = findComponentByName(this.library, parentModifier.peer.componentName)
if (parentComponent === undefined) {
continue
}
const layoutTarget = this.getLayoutTargetDescription(
parentModifier, false, parentComponent.attributeDeclaration)
const location = this.library.layout.resolve(layoutTarget)
const parentModifierName = this.generateAuxiliaryClassName(parentModifier)
importsCollector.addFeature(parentModifierName, location)
parentModifier = parentModifier.parent
}
collectDeclItself(this.library, idl.createReferenceType(getReferenceTo('ModifierState')), importsCollector)
importsCollector.addFeature("AttributeModifier", HandwrittenModule(this.library.language))
if (modifier.isTrivial === false) {
importsCollector.addFeature(this.generateAttributeSetDerivedName(modifier),
HandwrittenModule(this.library.language))
}
importsCollector.addFeature("PeerNode", "./PeerNode")
const peerLocation = this.library.layout.resolve({
node: component.attributeDeclaration,
role: LayoutNodeRole.COMPONENT,
})
importsCollector.addFeature(componentToPeerClass(component.name), `./${peerLocation}`)
importsCollector.addFeatures(["int32", "int64"], "@koalaui/common")
collectDeclDependencies(this.library, component.attributeDeclaration, importsCollector)
expandComponentWithSupers(this.library, component.attributeDeclaration).forEach(decl => {
collectDeclItself(this.library, decl, importsCollector)
})
collectDeclItself(this.library, component.attributeDeclaration, importsCollector)
return importsCollector
}
printModifierWithKeyBody(writer: LanguageWriter, peer: PeerClass, method: PeerMethod) {
const argsNames = method.argConvertors(this.library).map((conv, index) => {
const argName = conv.param
const castedType = idl.maybeOptional(method.method.signature.args[index], method.method.signature.isArgOptional(index))
return `${writer.escapeKeyword(argName)} as ${writer.getNodeName(castedType)}`
})
const call = writer.makeFunctionCall('modifierWithKey', [
writer.makeString(`this._modifiersWithKeys`),
writer.makeString(`${getAttributeModifierClassName(method)}.identity`),
writer.makeString(`${getAttributeModifierClassName(method)}.factory`),
...argsNames.map((arg) => writer.makeString(`${arg}`))
])
writer.writeExpressionStatement(call)
}
printModifierNullWidthKeyBody(writer: LanguageWriter, peer: PeerClass, method: PeerMethod) {
const call = writer.makeFunctionCall('modifierNullWithKey', [
writer.makeString(`this._modifiersWithKeys`),
writer.makeString(`${getAttributeModifierClassName(method)}.identity`)
])
writer.writeExpressionStatement(call)
}
needCollectParentMethods(modifier: ModifierInfo): boolean {
const peer = modifier.peer
const component = findComponentByName(this.library, peer.componentName)!
let counter = 0
if (modifier.parent) {
let [parentRef] = component.attributeDeclaration.inheritance
let parentDecl = this.library.resolveTypeReference(parentRef)
while (parentDecl) {
counter++;
const parentComponent = findComponentByDeclaration(this.library, parentDecl as idl.IDLInterface)!
if (parentComponent.attributeDeclaration.inheritance.length) {
let [parentRef] = parentComponent.attributeDeclaration.inheritance
parentDecl = this.library.resolveTypeReference(parentRef)
} else {
parentDecl = undefined
}
}
}
return counter > 1;
}
collectAttributes(peer: PeerClass, attributeTypes: Array<AttributeType>) {
const parentTypesNames = new Array<string>()
const component = findComponentByName(this.library, peer.componentName)!
let parentRef = component.attributeDeclaration.inheritance.at(0)
while (parentRef) {
const parentDecl = this.library.resolveTypeReference(parentRef) as idl.IDLInterface
const parentPeer = findPeerByComponentDeclaration(this.library, findComponentByDeclaration(this.library, parentDecl)!)!
groupOverloads(parentPeer.methods, this.library.language).forEach(m => {
if (!m[0].isCallSignature) {
parentTypesNames.push(m[0].method.name)
}
})
parentRef = parentDecl.inheritance.at(0)
}
const overloadCounter = new Map<string, number>()
for (const parentTypeName of parentTypesNames) {
if (!overloadCounter.has(parentTypeName))
overloadCounter.set(parentTypeName, 0)
overloadCounter.set(parentTypeName, overloadCounter.get(parentTypeName)! + 1)
}
const attributeFilter = (name: string) => {
return name.startsWith('set') && name.endsWith('Options')
}
groupOverloads(peer.methods, this.library.language).forEach(m => {
const method = m[0]
if (attributeFilter(method.method.name)) {
return
}
const args: string[] = []
let optional: boolean = true;
const types = method.argConvertors(this.library).map((conv, index) => {
args.push(conv.param)
const type = idl.maybeOptional(method.method.signature.args[index], method.method.signature.isArgOptional(index))
if (!isOptionalType(type)) optional = false;
return type
})
const functionName = method.method.name
let v = 0
if (overloadCounter.has(functionName)) v = overloadCounter.get(functionName)! + 1
overloadCounter.set(functionName, v)
attributeTypes.push({ peer, method: method, args: args, argTypes: types, isOptional: optional, overloadIndex: v })
})
}
generateHooksCall(hookName: string, params: LanguageExpression[], writer: LanguageWriter): LanguageExpression {
const hookCall = writer.makeFunctionCall(hookName, [
writer.makeThis(), writer.makeString('peer'), ...params
])
return hookCall;
}
printHookedMethodBody(method: Method, hookName: string, flagIndex: number, writer: LanguageWriter) {
const args = method.signature.args.map((_, i) => method.signature.argName(i))
const hookCall = writer.makeFunctionCall(hookName, [
writer.makeThis(), ...args.map(arg => writer.makeString(arg)), writer.makeString(`${flagIndex}`)
])
writer.writeExpressionStatement(hookCall)
}
noNeedPrintModifier(attribute: AttributeType) {
return !idl.isPrimitiveType(attribute.method.method.signature.returnType, 'this')
}
printApplyModifierPatch(
peer: PeerClass,
writer: LanguageWriter,
component: IdlComponentDeclaration,
attributeTypes: Array<AttributeType>,
hasParent: boolean,
collectedHooks: string[],
changeName: boolean) {
const totalCount = attributeTypes.length;
const batchSize = 20;
const batchCount = Math.ceil(totalCount / batchSize);
const methodName = changeName ? `applyModifierPatch${peer.componentName}` : `applyModifierPatch`
writer.writeMethodImplementation(new Method(methodName,
new NamedMethodSignature(idl.createPrimitiveType('void'), [idl.createReferenceType("arkui.PeerNode.PeerNode")], ['node'], [], [], [])),
writer => {
if (hasParent) {
writer.print(`super.applyModifierPatch${peer.parentNames?.componentName}(node)`);
}
writer.print(`this._state.addRef()`);
writer.print(`const peer = node as ${componentToPeerClass(component.name)};`)
writer.print(`const flagArray = this.${this.generateFilledFlagName(peer)};`);
for (let batchIndex = 0; batchIndex < batchCount; batchIndex++) {
writer.print(`this.${methodName}${batchIndex}(peer, flagArray);`);
}
}
)
for (let batchIndex = 0; batchIndex < batchCount; batchIndex++) {
writer.print(`${methodName}${batchIndex}(peer: ${componentToPeerClass(component.name)}, flagArray: Uint8Array): void {`)
writer.pushIndent()
{
const start = batchIndex * batchSize;
const end = Math.min(start + batchSize, totalCount);
const statements: IfStatement[] = []
for (let index = start; index < end; index++) {
const attribute = attributeTypes[index];
if (this.noNeedPrintModifier(attribute)) {
continue;
}
const expr = `${this.generateFiledFlag(attribute, index, true)} != ${AttributeUpdaterFlag.INITIAL}`
const params: LanguageExpression[] = attribute.args.map((_, index) => {
return this.castSetType(attribute, writer, attribute.method.method.signature, index)
})
const resetParams: LanguageExpression[] = attribute.args.map((_, index) => {
return this.castResetType(writer, attribute.method.method.signature, index)
})
const methodName = `${attribute.method.sig.name}Attribute`
const hookRecord = peerGeneratorConfiguration().hooks.get(peer!.originalClassName ?? '')?.get(attribute.method.method.name)
if (hookRecord && hookRecord.replaceImplementation) {
collectedHooks.push(hookRecord.hookName)
}
const statement = (hookRecord && hookRecord.replaceImplementation) ? this.generateHooksCall(hookRecord.hookName, params, writer) : writer.makeMethodCall('peer', methodName, params)
const resetStatement = writer.makeMethodCall('peer', methodName, resetParams)
const switchPrinter = this.library.createLanguageWriter();
switchPrinter.print(`switch (${this.generateFiledFlag(attribute, index, true)}) {`)
switchPrinter.pushIndent()
switchPrinter.print(`case ${AttributeUpdaterFlag.UPDATE}: {`)
switchPrinter.pushIndent()
switchPrinter.print(`${statement.asString()};`)
switchPrinter.print(`${this.generateFiledFlag(attribute, index, true)} = ${AttributeUpdaterFlag.RESET};`)
switchPrinter.print(`break;`)
switchPrinter.popIndent()
switchPrinter.print(`}`)
switchPrinter.print(`case ${AttributeUpdaterFlag.SKIP}: {`)
switchPrinter.pushIndent()
switchPrinter.print(`${this.generateFiledFlag(attribute, index, true)} = ${AttributeUpdaterFlag.RESET};`)
switchPrinter.print(`break;`)
switchPrinter.popIndent()
switchPrinter.print(`}`)
switchPrinter.print(`default: {`)
switchPrinter.pushIndent()
switchPrinter.print(`${this.generateFiledFlag(attribute, index, true)} = ${AttributeUpdaterFlag.INITIAL};`)
if (attribute.isOptional) {
if (hookRecord && hookRecord.replaceImplementation) {
switchPrinter.print(`${switchPrinter.makeFunctionCall(hookRecord.hookName, [writer.makeThis(), writer.makeString('peer'), ...resetParams]).asString()};`)
} else {
switchPrinter.print(`${resetStatement.asString()};`)
}
}
switchPrinter.popIndent()
switchPrinter.print(`}`)
switchPrinter.popIndent()
switchPrinter.print(`}`)
statements.push(new IfStatement(
writer.makeString(expr),
writer.makeBlock(switchPrinter.getOutput().map(s => writer.makeStatement(writer.makeString(s)))),
undefined,
undefined,
undefined
))
}
writer.writeStatements(...statements)
}
writer.popIndent()
writer.print(`}`)
}
}
printMergeModifier(
peer: PeerClass,
writer: LanguageWriter,
modifierName: string,
attributeTypes: Array<AttributeType>,
hasParent: boolean,
changeName: boolean) {
const totalCount = attributeTypes.length;
const batchSize = 20;
const batchCount = Math.ceil(totalCount / batchSize);
const methodName = changeName ? `mergeModifier${peer.componentName}` : `mergeModifier`
writer.print(`${methodName}(modifier: ${modifierName}): void {`)
writer.pushIndent()
{
if (hasParent) {
writer.print(`super.mergeModifier${peer.parentNames?.componentName}(modifier)`);
}
writer.print(`this._state = modifier._state;`);
writer.print(`const flagArray = modifier.${this.generateFilledFlagName(peer)};`);
for (let batchIndex = 0; batchIndex < batchCount; batchIndex++) {
writer.print(`this.${methodName}${batchIndex}(modifier, flagArray);`);
}
}
writer.popIndent()
writer.print(`}`)
for (let batchIndex = 0; batchIndex < batchCount; batchIndex++) {
writer.print(`${methodName}${batchIndex}(modifier: ${modifierName}, flagArray: Uint8Array): void {`)
writer.pushIndent()
{
const start = batchIndex * batchSize;
const end = Math.min(start + batchSize, totalCount);
const statements: IfStatement[] = []
for (let index = start; index < end; index++) {
const attribute = attributeTypes[index];
if (this.noNeedPrintModifier(attribute)) {
continue;
}
const expr = `${this.generateFiledFlag(attribute, index, true)} != ${AttributeUpdaterFlag.INITIAL}`
const params: LanguageExpression[] = attribute.args.map((_, index) => {
return writer.makeCast(
writer.makeString(`modifier.${this.generateFiledName(attribute, index.toString())}`),
idl.maybeOptional(attribute.method.method.signature.args[index], attribute.method.method.signature.isArgOptional(index)),
)
})
const resetParams: LanguageExpression[] = attribute.args.map((_, index) => {
return this.castResetType(writer, attribute.method.method.signature, index)
})
const statement = writer.makeMethodCall('this', attribute.method.method.name, params)
const resetStatement = writer.makeMethodCall('this', attribute.method.method.name, resetParams)
const switchPrinter = this.library.createLanguageWriter();
switchPrinter.print(`switch (${this.generateFiledFlag(attribute, index, true)}) {`)
switchPrinter.pushIndent()
switchPrinter.print(`case ${AttributeUpdaterFlag.UPDATE}:`)
switchPrinter.print(`case ${AttributeUpdaterFlag.SKIP}: {`)
switchPrinter.pushIndent()
switchPrinter.print(`${statement.asString()};`)
switchPrinter.print(`break;`)
switchPrinter.popIndent()
switchPrinter.print(`}`)
switchPrinter.print(`default: {`)
if (attribute.isOptional) {
switchPrinter.pushIndent()
switchPrinter.print(`${resetStatement.asString()};`)
switchPrinter.popIndent()
}
switchPrinter.print(`}`)
switchPrinter.popIndent()
switchPrinter.print(`}`)
statements.push(new IfStatement(
writer.makeString(expr),
writer.makeBlock(switchPrinter.getOutput().map(s => writer.makeStatement(writer.makeString(s)))),
undefined,
undefined,
undefined
))
}
writer.writeStatements(...statements)
}
writer.popIndent()
writer.print(`}`)
}
}
private hasHeirs(peer: PeerClass) {
const component = findComponentByName(this.library, peer.componentName)
if (!component) {
throw new Error(`Can not find component with name ${peer.componentName}`)
}
return collectComponents(this.library).some(it => {
return getSuper(it.attributeDeclaration, this.library) === component?.attributeDeclaration
})
}
private hasParent(peer: PeerClass) {
const component = findComponentByName(this.library, peer.componentName)
if (!component) {
throw new Error(`Can not find component with name ${peer.componentName}`)
}
return getSuper(component.attributeDeclaration, this.library) !== undefined
}
printModifiers(modifierInfo: ModifierInfo): PrinterResult[] {
const peer = modifierInfo.peer
const component = findComponentByName(this.library, peer.componentName)!
const isAbstract = !this.hasHeirs(peer) && !(modifierInfo.isTrivial ?? true)
const modifierName = this.generateAttributeSetName(modifierInfo)
const baseModifierMethods = [
'applyNormalAttribute',
'applyPressedAttribute',
'applyFocusedAttribute',
'applyDisabledAttribute',
'applySelectedAttribute'
]
const componentAttribute = component.attributeDeclaration;
let abstractMethods = modifierInfo.modifier?.methods ?? []
const classWithLogicName = this.hasHeirs(peer) ?
this.generateAuxiliaryClassName(modifierInfo) :
modifierName
const generateLogic: PrinterResult['generate'] = () => {
const printer = this.library.createLanguageWriter();
const nameConvertor = this.library.createTypeNameConvertor(this.library.language)
const compAttributteConverted = nameConvertor.convert(componentAttribute)
let extendsInterface: string[] = this.hasHeirs(peer) ?
[`${compAttributteConverted}`] :
[`${compAttributteConverted}`, `AttributeModifier<${compAttributteConverted}>`]
const attributeTypes: Array<AttributeType> = new Array
const noNeedPrintModifier = (attribute: AttributeType) => {
return !idl.isPrimitiveType(attribute.method.method.signature.returnType, 'this')
}
this.collectAttributes(peer, attributeTypes)
const collectedHooks: string[] = []
abstractMethods = abstractMethods.filter(method => {
return (!baseModifierMethods.includes(method.name) || method.parameters.length != 1)
})
const parentSet = this.generateAttributeSetParentName(modifierInfo)
printer.writeClass(classWithLogicName, (writer) => {
if (!this.hasParent(peer)) {
writer.print("_instanceId: number = -1;")
writer.print("_state: ModifierState = new ModifierState;")
}
writer.print(`private ${this.generateAddrName(peer)}: ArrayBuffer = new ArrayBuffer(4096);`)
writer.print(`public ${this.generateFilledFlagName(peer)}: Uint8Array = new Uint8Array(this.${this.generateAddrName(peer)});`)
if (isAbstract) {
writer.writeStaticInitBlock(writer => {
writer.print(
`const _check: ${
this.generateAttributeSetDerivedName(modifierInfo)} | undefined = undefined`)
})
}
writer.print(`constructor() {`)
writer.pushIndent()
if (modifierInfo.parent) writer.print(`super();`)
writer.print(`this.${this.generateFilledFlagName(peer)}.fill(0);`)
writer.popIndent()
writer.print(`}`)
if (!this.hasParent(peer)) {
writer.writeMethodImplementation(new Method(
`setInstanceId`,
new NamedMethodSignature(idl.createPrimitiveType('void'),
[idl.createPrimitiveType('number')], ['instanceId'], [], [], [])),
writer => {
writer.writeStatement(writer.makeAssign('this._instanceId', undefined, writer.makeString('instanceId'), false))
}
)
}
if (!this.hasHeirs(peer)) {
writer.print(`isUpdater: () => boolean = () => false`)
baseModifierMethods.forEach(method => {
const methodName = this.hasHeirs(peer) ? `${method}${peer.componentName}` : method
writer.print(`${methodName}(instance: ${compAttributteConverted}): void { }`)
})
}
attributeTypes.forEach((attribute, index) => {
attribute.argTypes.forEach((t, index) => {
const onlyNull = idl.isOptionalType(t) && idl.hasExtAttribute(t, idl.IDLExtendedAttributes.UnionOnlyNull)
const initExpr = onlyNull ? writer.makeNull(t) : undefined
writer.writeFieldDeclaration(this.generateFiledName(attribute, index.toString()), t, [], !onlyNull, initExpr)
})
})
this.printApplyModifierPatch(peer, writer, component, attributeTypes, modifierInfo.parent !== undefined, collectedHooks, this.hasHeirs(peer))
this.printMergeModifier(peer, writer, classWithLogicName, attributeTypes, modifierInfo.parent !== undefined, this.hasHeirs(peer))
attributeTypes.forEach((attribute, index) => {
printer.writeMethodImplementation(attribute.method.method, (writer) => {
if (noNeedPrintModifier(attribute)) {
writer.writeStatement(writer.makeThrowError("Not implemented"))
return;
}
let nameWoBase = modifierName
if (modifierName.endsWith('Base')) {
nameWoBase = modifierName.substring(0, modifierName.length - 'Base'.length)
}
const hookMethod = getHookMethod(nameWoBase, attribute.method.method.name)
if (hookMethod) {
this.printHookedMethodBody(attribute.method.method, hookMethod.hookName, index, writer)
collectedHooks.push(hookMethod.hookName)
writer.writeStatement(writer.makeReturn(writer.makeThis()))
return;
}
const equalStatements: LanguageExpression[] = []
equalStatements.push(writer.makeEquals([writer.makeString(`${this.generateFiledFlag(attribute, index)}`), writer.makeString(`${AttributeUpdaterFlag.INITIAL}`)]))
attribute.argTypes.forEach((t, index) => {
if (isPrimitiveType(t)) {
console.log("isPrimitiveType", `this.${this.generateFiledName(attribute, index.toString())}`)
equalStatements.push(writer.makeNaryOp("!==", [writer.makeString(`this.${this.generateFiledName(attribute, index.toString())}`), writer.makeString(attribute.args[index])]))
} else {
equalStatements.push(writer.makeString('true'))
}
})
const equalNary = writer.makeNaryOp('||', equalStatements)
const thenStatements: LanguageStatement[] = []
thenStatements.push(writer.makeAssign(`${this.generateFiledFlag(attribute, index)}`, undefined, writer.makeString(`${AttributeUpdaterFlag.UPDATE}`), false))
attribute.argTypes.forEach((t, index) => {
thenStatements.push(writer.makeAssign(`this.${this.generateFiledName(attribute, index.toString())}`, t, writer.makeString(attribute.args[index]), false))
})
thenStatements.push(writer.makeStatement(writer.makeString(`this._state.fireChange()`)))
const thenStatementBlock = writer.makeBlock(thenStatements)
const elseStatementBlock = writer.makeBlock([writer.makeAssign(`${this.generateFiledFlag(attribute, index)}`, undefined, writer.makeString(`${AttributeUpdaterFlag.SKIP}`), false)])
const condition = writer.makeCondition(equalNary, thenStatementBlock, elseStatementBlock)
writer.writeStatement(condition)
writer.writeStatement(writer.makeReturn(writer.makeThis()))
})
})
const attributeModifierSignature = generateAttributeModifierSignature(this.library, component)
const attributeModifierMethodName = this.hasHeirs(peer) ? `attributeModifier${peer.componentName}` : `attributeModifier`
writer.writeMethodImplementation(new Method(attributeModifierMethodName, attributeModifierSignature, [MethodModifier.PUBLIC]), writer => {
writer.writeStatement(writer.makeThrowError("Not implemented"))
})
if (!this.hasHeirs(peer)) {
abstractMethods.forEach((method => {
const signature = new NamedMethodSignature(method.returnType,
method.parameters.map(param => param.type),
method.parameters.map(param => param.name))
writer.writeMethodDeclaration(method.name, signature, [MethodModifier.ABSTRACT])
}))
}
}, parentSet, this.hasHeirs(peer) ? [compAttributteConverted] : extendsInterface, undefined, undefined, !this.hasHeirs(peer) && abstractMethods.length > 0)
const collector = this.printImports(modifierInfo)
collector.addFeatures(collectedHooks, HandwrittenModule(this.library.language))
return {
content: printer,
imports: collector,
}
}
const needInterface = modifierInfo.modifier !== undefined && modifierInfo.isTrivial === true
const logicPrinter: PrinterResult = {
generate: generateLogic,
over: this.getLayoutTargetDescription(modifierInfo, needInterface, component.attributeDeclaration)
}
let results = [ logicPrinter ]
if (modifierInfo.modifier && this.hasHeirs(peer)) {
const generateInterface: PrinterResult['generate'] = () => {
const printer = this.library.createLanguageWriter()
const nameConvertor = this.library.createTypeNameConvertor(this.library.language)
const compAttributteConverted = nameConvertor.convert(componentAttribute)
let extendsInterface: string[] = [
`${compAttributteConverted}`, `AttributeModifier<${compAttributteConverted}>`
]
printer.writeClass(modifierName, (writer) => {
if (!(modifierInfo.isTrivial ?? true)) {
writer.writeStaticInitBlock(writer => {
writer.print(
`const _check: ${
this.generateAttributeSetDerivedName(modifierInfo)} | undefined = undefined`)
})
}
writer.print(`isUpdater: () => boolean = () => false`)
for (const method of baseModifierMethods) {
writer.print(`${method}(instance: ${compAttributteConverted}): void { }`)
}
writer.writeMethodImplementation(new Method(`applyModifierPatch`,
new NamedMethodSignature(
idl.createPrimitiveType('void'),
[idl.createReferenceType("arkui.PeerNode.PeerNode")],
['node'], [], [], [])),
writer => {
writer.print(`super.applyModifierPatch${peer.componentName}(node)`)
}
)
writer.print(
`mergeModifier(modifier: ${this.generateAttributeSetDerivedName(modifierInfo)}): void {`)
writer.pushIndent()
{
writer.print(`super.mergeModifier${peer.componentName}(modifier)`)
}
writer.popIndent()
writer.print('}')
abstractMethods.forEach((method => {
const signature = new NamedMethodSignature(method.returnType,
method.parameters.map(param => param.type),
method.parameters.map(param => param.name))
writer.writeMethodDeclaration(method.name, signature, [MethodModifier.ABSTRACT])
}))
}, classWithLogicName, extendsInterface, undefined, undefined, abstractMethods.length > 0)
const collector = this.printImports(modifierInfo)
return {
content: printer,
imports: collector,
}
}
results.push({
generate: generateInterface,
over: this.getLayoutTargetDescription(modifierInfo, true, component.attributeDeclaration)
})
}
return results
}
}
export function printModifiers(peerLibrary: PeerLibrary): PrinterResult[] {
if (peerLibrary.language !== Language.ARKTS) {
return []
}
let result: PrinterResult[] = []
collectModifiers(peerLibrary).forEach((modifiers, _file) => {
const visitor = new ModifiersFileVisitor(peerLibrary, modifiers)
result.push(...visitor.visit())
})
return result
}
function isOptionOnlyNull(attribute: AttributeType): boolean {
const args = attribute.method.method.signature.args
if (args.length == 0) return false
const type = args[0]
return idl.hasExtAttribute(type, idl.IDLExtendedAttributes.UnionOnlyNull)
}