From a020e49bdc6987c1681ee42c05202a2df2d29ed0 Mon Sep 17 00:00:00 2001 From: tangcent Date: Sun, 15 Dec 2024 19:16:32 +0800 Subject: [PATCH] fix: update methods in ScriptRuleParser to avoid return types with parameter type erasure (#1176) --- .../idea/plugin/rule/ScriptRuleParser.kt | 1401 +++++++++-------- 1 file changed, 723 insertions(+), 678 deletions(-) diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt index ade07190..599d2324 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt @@ -35,34 +35,39 @@ import javax.script.ScriptContext import javax.script.ScriptEngine import javax.script.SimpleScriptContext +/** + * ScriptRuleParser is an abstract class that extends AbstractRuleParser and provides the + * functionality for parsing rules defined in scripts. + */ abstract class ScriptRuleParser : AbstractRuleParser() { @Inject - protected val duckTypeHelper: DuckTypeHelper? = null + protected lateinit var duckTypeHelper: DuckTypeHelper @Inject - protected val psiClassHelper: PsiClassHelper? = null + protected lateinit var psiClassHelper: PsiClassHelper @Inject - protected val classRuleConfig: ClassRuleConfig? = null + protected lateinit var classRuleConfig: ClassRuleConfig @Inject - protected val docHelper: DocHelper? = null + protected lateinit var docHelper: DocHelper @Inject - protected val annotationHelper: AnnotationHelper? = null + protected lateinit var annotationHelper: AnnotationHelper @Inject - protected val jvmClassHelper: JvmClassHelper? = null + protected lateinit var jvmClassHelper: JvmClassHelper @Inject - protected val methodReturnInferHelper: MethodInferHelper? = null + protected lateinit var methodReturnInferHelper: MethodInferHelper @Inject protected lateinit var actionContext: ActionContext @Inject - protected val logger: Logger? = null + protected lateinit var logger: Logger + override fun parseAnyRule(rule: String): AnyRule? { return { context -> eval(rule, context) @@ -88,10 +93,15 @@ abstract class ScriptRuleParser : AbstractRuleParser() { } } + /** + * Evaluates a script against a given rule context using a script engine + * within a properly configured context. + */ private fun eval(ruleScript: String, context: RuleContext): Any? { return try { val simpleScriptContext = SimpleScriptContext() + // Setting script context attributes based on the provided rule context. context.exts()?.forEach { simpleScriptContext.setAttribute( it.key, @@ -104,6 +114,7 @@ abstract class ScriptRuleParser : AbstractRuleParser() { ) } + // Determining the appropriate context for the script execution. val contextForScript: RuleContext? = (context as? BaseScriptRuleContext) ?: context.getCore()?.let { contextOf(it, context.getResource()) } ?: context.getResource()?.let { contextOf(it, context.getResource()) } @@ -111,23 +122,35 @@ abstract class ScriptRuleParser : AbstractRuleParser() { simpleScriptContext.setAttribute("it", contextForScript, ScriptContext.ENGINE_SCOPE) } + // Initializing and executing the script. initScriptContext(simpleScriptContext, context) getScriptEngine().eval(ruleScript, simpleScriptContext) } catch (e: UnsupportedScriptException) { - logger?.error("unsupported script type:${e.getType()},script:$ruleScript") + logger.error("unsupported script type:${e.getType()},script:$ruleScript") null } catch (e: Exception) { - logger?.traceError("error eval script:$ruleScript", e) + logger.traceError("error eval script:$ruleScript", e) null } } + /** + * Abstract method for providing the script engine. + * Must be implemented by subclasses to define the script evaluation mechanism. + */ protected abstract fun getScriptEngine(): ScriptEngine + /** + * Optional method to initialize the script context. + * Can be overridden by subclasses to add custom context initialization logic. + */ protected open fun initScriptContext(scriptContext: ScriptContext, context: RuleContext) { } + /** + * Provides a context representation for a given target object and its associated PsiElement. + */ override fun contextOf(target: Any, context: PsiElement?): RuleContext { return when (target) { is PsiClass -> ScriptPsiClassContext(target) @@ -146,6 +169,10 @@ abstract class ScriptRuleParser : AbstractRuleParser() { } } + /** + * Wraps objects for script evaluation. Converts JVM/Psi-specific objects into a format + * compatible with the script context. Handles complex types like collections and maps. + */ private fun wrap(obj: Any?, context: PsiElement?, shouldCopy: Boolean = true): Any? { if (obj == null) { return null @@ -213,21 +240,25 @@ abstract class ScriptRuleParser : AbstractRuleParser() { constructor(psiElement: PsiElement?) : super(psiElement) constructor() : super() + /** + * Wraps objects in the current context to adapt them for use in scripts. + */ @Suppress("UNCHECKED_CAST") private fun wrapAs(obj: Any?): T? { return wrap(obj, getPsiContext()) as T? } - fun name(): String { return getName() ?: "" } /** + * Checks if the context has the specified annotation. + * * it.hasAnn("annotation_name"):Boolean */ fun hasAnn(name: String): Boolean { - return annotationHelper!!.hasAnn(getResource(), name) + return annotationHelper.hasAnn(getResource(), name) } /** @@ -238,24 +269,26 @@ abstract class ScriptRuleParser : AbstractRuleParser() { } /** + * Retrieves a map representation of the specified annotation's attributes. + * * it.annMap("annotation_name"):Map? */ fun annMap(name: String): Map? { - return wrapAs(annotationHelper!!.findAnnMap(getResource(), name)) + return wrapAs(annotationHelper.findAnnMap(getResource(), name)) } /** * it.annMaps("annotation_name"):List>? */ fun annMaps(name: String): List>? { - return wrapAs(annotationHelper!!.findAnnMaps(getResource(), name)) + return wrapAs(annotationHelper.findAnnMaps(getResource(), name)) } /** * it.ann("annotation_name","attr"):String? */ fun ann(name: String, attr: String): String? { - return annotationHelper!!.findAttrAsString(getResource(), name, attr) + return annotationHelper.findAttrAsString(getResource(), name, attr) } /** @@ -269,42 +302,42 @@ abstract class ScriptRuleParser : AbstractRuleParser() { * it.ann("annotation_name","attr"):Any? */ fun annValue(name: String, attr: String): Any? { - return wrapAs(annotationHelper!!.findAttr(getResource(), name, attr)) + return wrapAs(annotationHelper.findAttr(getResource(), name, attr)) } /** * it.doc():String */ fun doc(): String? { - return docHelper!!.getAttrOfDocComment(getResource()) + return docHelper.getAttrOfDocComment(getResource()) } /** * it.doc("tag"):String? */ fun doc(tag: String): String? { - return docHelper!!.findDocByTag(getResource(), tag) + return docHelper.findDocByTag(getResource(), tag) } /** * it.docs("tag"):List? */ fun docs(tag: String): List? { - return docHelper!!.findDocsByTag(getResource(), tag) + return docHelper.findDocsByTag(getResource(), tag) } /** * it.hasDoc("tag"):Boolean */ fun hasDoc(tag: String): Boolean { - return docHelper!!.hasTag(getResource(), tag) + return docHelper.hasTag(getResource(), tag) } /** * it.doc("tag","subTag"):String? */ fun doc(tag: String, subTag: String): String? { - return docHelper!!.findDocsByTagAndName( + return docHelper.findDocsByTagAndName( getResource(), tag, subTag ) } @@ -314,15 +347,18 @@ abstract class ScriptRuleParser : AbstractRuleParser() { } fun modifiers(): List { - return getResource()?.let { jvmClassHelper!!.extractModifiers(it) } ?: emptyList() + return getResource()?.let { jvmClassHelper.extractModifiers(it) } ?: emptyList() } + /** + * Retrieves the source code associated with this context. + */ fun sourceCode(): String? { return actionContext.callInReadUI { getResource()?.text } } fun defineCode(): String? { - return getResource()?.let { jvmClassHelper!!.defineCode(it) } + return getResource()?.let { jvmClassHelper.defineCode(it) } } open fun contextType(): String { @@ -330,6 +366,133 @@ abstract class ScriptRuleParser : AbstractRuleParser() { } } + /** + * ScriptPsiClassContext represents a PsiClass within the script context. + * Provides methods to interact with class properties, methods, fields, and hierarchy. + */ + @ScriptTypeName("class") + abstract inner class ScriptClassContext : BaseScriptRuleContext { + constructor(psiElement: PsiElement?) : super(psiElement) + constructor() : super() + + abstract fun methods(): Array + + abstract fun fields(): Array + + /** + * Checks if the current class extends the specified superclass. + */ + abstract fun isExtend(superClass: String): Boolean + + abstract fun isMap(): Boolean + + abstract fun isCollection(): Boolean + + abstract fun isArray(): Boolean + + /** + * Returns whether this class is a primitive + */ + abstract fun isPrimitive(): Boolean + + /** + * Returns whether this class is a primitive wrapper + */ + abstract fun isPrimitiveWrapper(): Boolean + + /** + * Returns whether the given {@code type} is a primitive or primitive wrapper + * or {@code String}、{@code Object} + */ + abstract fun isNormalType(): Boolean + + /** + * Checks if the class is an interface. + * + * @return true if the class is an interface, false otherwise. + */ + abstract fun isInterface(): Boolean + + /** + * Checks if the class is an annotation type. + * + * @return true if the class is an annotation type, false otherwise + */ + abstract fun isAnnotationType(): Boolean + + /** + * Checks if the class is an enumeration. + * + * @return true if the class is an enumeration, false otherwise. + */ + abstract fun isEnum(): Boolean + + abstract fun fieldCnt(): Int + + abstract fun methodCnt(): Int + + fun toObject(): Any? { + return toObject(readGetter = true, readSetter = true, readComment = false) + } + + abstract fun toObject( + readGetter: Boolean, readSetter: Boolean, + readComment: Boolean, + ): Any? + + fun toJson(): String? { + return toJson(readGetter = true, readSetter = true) + } + + fun toJson(readGetter: Boolean, readSetter: Boolean): String? { + return toObject(readGetter, readSetter, false) + ?.let { RequestUtils.parseRawBody(it) } + } + + fun toJson5(): String? { + return toJson5(false, false) + } + + fun toJson5(readGetter: Boolean, readSetter: Boolean): String? { + return toObject(readGetter, readSetter, true)?.let { + actionContext.instance(Json5Formatter::class).format(it) + } + } + + /** + * Returns the base class of this class. + * If this class represents either the + * [Object] class, an interface, a primitive type, or void, then + * null is returned. + * {class A extend B} -> B + * {class A} -> java.lang.Object + * {class A implement IA} -> java.lang.Object + * {class A extend B implement IA} -> B + * {interface IA} -> null + * {interface IA extend IB} -> null + * + * @return the base class. May return null when jdk is not configured, so no java.lang.Object is found, + * or for java.lang.Object itself + */ + abstract fun superClass(): ScriptClassContext? + + /** + * Returns the list of classes that this class or interface extends. + * + * @return the extends list, or null for anonymous classes. + */ + abstract fun extends(): Array? + + /** + * Returns the list of interfaces that this class implements. + * + * @return the implements list, or null for anonymous classes + */ + abstract fun implements(): Array? + + abstract fun mavenId(): MavenIdData? + } + /** * it.methods():method[] * it.methodCnt():int @@ -349,33 +512,36 @@ abstract class ScriptRuleParser : AbstractRuleParser() { } override fun methods(): Array { - return jvmClassHelper!!.getAllMethods(psiClass).mapToTypedArray { ScriptPsiMethodContext(it) } + return jvmClassHelper.getAllMethods(psiClass).mapToTypedArray { ScriptPsiMethodContext(it) } } override fun methodCnt(): Int { - return jvmClassHelper!!.getAllMethods(psiClass).size + return jvmClassHelper.getAllMethods(psiClass).size } override fun fields(): Array { - return jvmClassHelper!!.getAllFields(psiClass).mapToTypedArray { ScriptPsiFieldContext(it) } + return jvmClassHelper.getAllFields(psiClass).mapToTypedArray { ScriptPsiFieldContext(it) } } override fun fieldCnt(): Int { - return jvmClassHelper!!.getAllFields(psiClass).size + return jvmClassHelper.getAllFields(psiClass).size } + /** + * Checks if the current class extends the specified superclass. + */ override fun isExtend(superClass: String): Boolean { - return ActionContext.getContext()!!.callInReadUI { - jvmClassHelper!!.isInheritor(psiClass, superClass) + return actionContext.callInReadUI { + jvmClassHelper.isInheritor(psiClass, superClass) } ?: false } override fun isMap(): Boolean { - return jvmClassHelper!!.isMap(PsiTypesUtil.getClassType(psiClass)) + return jvmClassHelper.isMap(PsiTypesUtil.getClassType(psiClass)) } override fun isCollection(): Boolean { - return jvmClassHelper!!.isCollection(PsiTypesUtil.getClassType(psiClass)) + return jvmClassHelper.isCollection(PsiTypesUtil.getClassType(psiClass)) } override fun isArray(): Boolean { @@ -386,14 +552,14 @@ abstract class ScriptRuleParser : AbstractRuleParser() { * Returns whether this class is a primitive */ override fun isPrimitive(): Boolean { - return jvmClassHelper!!.isPrimitive(name()) + return jvmClassHelper.isPrimitive(name()) } /** * Returns whether this class is a primitive wrapper */ override fun isPrimitiveWrapper(): Boolean { - return jvmClassHelper!!.isPrimitiveWrapper(name()) + return jvmClassHelper.isPrimitiveWrapper(name()) } /** @@ -401,7 +567,7 @@ abstract class ScriptRuleParser : AbstractRuleParser() { * or {@code String}、{@code Object} */ override fun isNormalType(): Boolean { - return jvmClassHelper!!.isNormalType(name()) + return jvmClassHelper.isNormalType(name()) } override fun isInterface(): Boolean { @@ -433,11 +599,11 @@ abstract class ScriptRuleParser : AbstractRuleParser() { readGetter: Boolean, readSetter: Boolean, readComment: Boolean, ): Any? { - return jvmClassHelper!!.resolveClassToType(psiClass)?.let { - psiClassHelper!!.getTypeObject( + return jvmClassHelper.resolveClassToType(psiClass)?.let { + psiClassHelper.getTypeObject( it, psiClass, JsonOption.NONE.or(readGetter, readSetter, readComment) ) - } ?: psiClassHelper!!.getFields(psiClass, JsonOption.NONE.or(readGetter, readSetter, readComment)) + } ?: psiClassHelper.getFields(psiClass, JsonOption.NONE.or(readGetter, readSetter, readComment)) } override fun superClass(): ScriptClassContext? { @@ -511,7 +677,7 @@ abstract class ScriptRuleParser : AbstractRuleParser() { readGetter: Boolean, readSetter: Boolean, readComment: Boolean, ): Any? { - return psiClassHelper!!.getTypeObject( + return psiClassHelper.getTypeObject( explicitClass.asDuckType(), psiClass, JsonOption.NONE.or(readGetter, readSetter, readComment) ) ?: super.toObject(readGetter, readSetter, readComment) } @@ -555,527 +721,201 @@ abstract class ScriptRuleParser : AbstractRuleParser() { } /** - * it.type:class - * it.containingClass:class - * it.jsonName:String + * Tool function to get the explicit class of the given duck type */ - @ScriptTypeName("field") - open inner class ScriptPsiFieldContext(private val psiField: PsiField) : BaseScriptRuleContext(psiField) { + private fun DuckType.explicit(): ExplicitClass? { + return (this as? SingleDuckType)?.let { duckTypeHelper.explicit(it) } + } + + /** + * Inner class representing the base script context used across all rules. + * Provides methods to interact with annotations, documentation, and modifiers. + * + * it.methods():method[] + * it.isExtend(""):Boolean + * it.isSuper(""):Boolean + * it.isMap():Boolean + * it.isCollection():Boolean + * it.isArray():Boolean + * @see ScriptPsiClassContext + */ + @ScriptTypeName("class") + inner class ScriptPsiTypeContext(private val psiType: PsiType) : ScriptClassContext() { + override fun contextType(): String { - return "field" + return "class" } - open fun type(): ScriptClassContext { - return ScriptPsiTypeContext(psiField.type) + @ScriptIgnore + override fun getPsiContext(): PsiElement? { + return getResource() ?: jvmClassHelper.resolveClassInType(psiType) } - @ScriptIgnore - override fun asPsiModifierListOwner(): PsiModifierListOwner? { - return psiField + private var duckType: DuckType? = null + + private val explicitClassContext by lazy { + duckType?.explicit()?.let { ScriptExplicitClassContext(it) } + } + + init { + duckType = duckTypeHelper.ensureType(psiType) + if (duckType != null && duckType is SingleDuckType) { + this.psiElement = (duckType as SingleDuckType).psiClass() + } } override fun getName(): String? { - return psiField.name + return duckType?.canonicalText() } - /** - * Returns the class containing the member. - * - * @return the containing class. - */ - open fun containingClass(): ScriptPsiClassContext { - return ScriptPsiClassContext(psiField.containingClass!!) + override fun getSimpleName(): String? { + return getDuckTypeSimpleName(duckType) } - /** - * Returns the class define the member. - * - * @return the defining class. - */ - open fun defineClass(): ScriptPsiClassContext { - return containingClass() + private fun getDuckTypeSimpleName(duckType: DuckType?): String? { + return when (duckType) { + null -> null + is SingleUnresolvedDuckType -> duckType.psiType().presentableText + is SingleDuckType -> duckType.psiClass().name + is ArrayDuckType -> getDuckTypeSimpleName(duckType.componentType()) + "[]" + else -> duckType.toString() + } } - /** - * attention:it should not be used in [json.rule.field.name] - */ - fun jsonName(): String { - return psiClassHelper!!.getJsonFieldName(psiField) + override fun methods(): Array { + return explicitClassContext?.methods() ?: emptyArray() } - fun jsonType(): ScriptPsiTypeContext { - return ScriptPsiTypeContext(classRuleConfig!!.tryConvert(psiField.type, psiField)) + override fun fields(): Array { + return explicitClassContext?.fields() ?: emptyArray() } - fun isEnumField(): Boolean { - return psiField is PsiEnumConstant + /** + * Checks if the current class extends the specified superclass. + */ + override fun isExtend(superClass: String): Boolean { + return jvmClassHelper.isInheritor(psiType, superClass) } - fun asEnumField(): ScriptPsiEnumConstantContext? { - return (psiField as? PsiEnumConstant)?.let { ScriptPsiEnumConstantContext(it) } + override fun isMap(): Boolean { + return jvmClassHelper.isMap(psiType) } - override fun toString(): String { - return containingClass().name() + "#" + psiField.name + override fun isCollection(): Boolean { + return jvmClassHelper.isCollection(psiType) } - } - @ScriptTypeName("field") - inner class ScriptExplicitFieldContext(private val explicitField: ExplicitField) : - ScriptPsiFieldContext(explicitField.psi()) { + override fun isArray(): Boolean { + return duckType is ArrayDuckType + } - override fun containingClass(): ScriptPsiClassContext { - return ScriptExplicitClassContext(explicitField.containClass()) + /** + * Returns whether the given {@code type} is a primitive or primitive wrapper + * or {@code String}、{@code Object} + */ + override fun isNormalType(): Boolean { + return jvmClassHelper.isNormalType(name()) } - override fun defineClass(): ScriptPsiClassContext { - return ScriptExplicitClassContext(explicitField.defineClass()) + override fun isInterface(): Boolean { + return (getResource() as? PsiClass)?.isInterface ?: false } - override fun type(): ScriptClassContext { - return ScriptDuckTypeContext(explicitField.getType(), explicitField.psi()) + override fun isAnnotationType(): Boolean { + return (getResource() as? PsiClass)?.isAnnotationType ?: false } - @ScriptIgnore - override fun getCore(): Any { - return explicitField - } - } - - @ScriptTypeName("enumField") - inner class ScriptPsiEnumConstantContext(private val psiEnumConstant: PsiEnumConstant) : - BaseScriptRuleContext(psiEnumConstant) { - - override fun getName(): String { - return psiEnumConstant.name - } - - fun ordinal(): Int { - return actionContext.callInReadUI { - psiEnumConstant.containingClass?.fields?.indexOf(psiEnumConstant) - } ?: 0 - } - - private fun getEnumFields(): Map { - return actionContext.notReentrant("enumFields") { - actionContext.callInReadUI { - actionContext.instance(PsiResolver::class).resolveEnumFields( - ordinal(), psiEnumConstant - ) - } - } ?: emptyMap() - - } - - fun getParams(): Map { - return getEnumFields().getSub("params") ?: emptyMap() - } - - fun getParam(name: String): Any? { - return getParams()[name] - } - } - - /** - * it.returnType:class - * it.isVarArgs:Boolean - * it.args:arg[] - * it.argTypes:class[] - * it.argCnt:int - * it.containingClass:class - */ - @ScriptTypeName("method") - open inner class ScriptPsiMethodContext(protected val psiMethod: PsiMethod) : BaseScriptRuleContext(psiMethod) { - - override fun contextType(): String { - return "method" - } - - /** - * Returns the return type of the method. - * - * @return the method return type, or null if the method is a constructor. - */ - open fun returnType(): ScriptClassContext? { - return psiMethod.getResolvedReturnType()?.let { ScriptPsiTypeContext(it) } - } - - /** - * Checks if the method accepts a variable number of arguments. - * - * @return true if the method is varargs, false otherwise. - */ - fun isVarArgs(): Boolean { - return psiMethod.isVarArgs + override fun isEnum(): Boolean { + return (getResource() as? PsiClass)?.isEnum ?: false } /** - * Returns the array of method parameters - */ - open fun args(): Array { - return psiMethod.parameterList.parameters.mapToTypedArray { ScriptPsiParameterContext(it) } - } - - /** - * Returns the array of method parameters type - */ - open fun argTypes(): Array { - return psiMethod.parameterList.parameters.mapToTypedArray { ScriptPsiTypeContext(it.type) } - } - - /** - * Returns the number of method parameters - */ - fun argCnt(): Int { - return psiMethod.parameterList.parametersCount - } - - /** - * Returns the class containing the member. - * - * @return the containing class. + * Returns whether this class is a primitive */ - open fun containingClass(): ScriptPsiClassContext { - return ScriptPsiClassContext(psiMethod.containingClass!!) + override fun isPrimitive(): Boolean { + return jvmClassHelper.isPrimitive(name()) } /** - * Returns the class define the member. - * - * @return the defining class. + * Returns whether this class is a primitive wrapper */ - open fun defineClass(): ScriptPsiClassContext { - return containingClass() - } - - @ScriptIgnore - override fun asPsiModifierListOwner(): PsiModifierListOwner? { - return psiMethod - } - - override fun getName(): String? { - return actionContext.callInReadUI { psiMethod.name } - } - - fun jsonName(): String { - return psiClassHelper!!.getJsonFieldName(psiMethod) + override fun isPrimitiveWrapper(): Boolean { + return jvmClassHelper.isPrimitiveWrapper(name()) } - fun type(): ScriptClassContext? { - return returnType() + override fun fieldCnt(): Int { + return (getResource() as? PsiClass)?.let { psiElement -> + return@let jvmClassHelper.getAllFields(psiElement).mapToTypedArray { ScriptPsiFieldContext(it) } + }?.size ?: 0 } - fun jsonType(): ScriptPsiTypeContext? { - return psiMethod.returnType?.let { classRuleConfig!!.tryConvert(it, psiMethod) } - ?.let { ScriptPsiTypeContext(it) } + override fun methodCnt(): Int { + return (getResource() as? PsiClass)?.let { psiElement -> + return@let jvmClassHelper.getAllMethods(psiElement).mapToTypedArray { ScriptPsiMethodContext(it) } + }?.size ?: 0 } - open fun returnObject( - needInfer: Boolean = false, readGetter: Boolean = true, - readSetter: Boolean = true, + override fun toObject( + readGetter: Boolean, + readSetter: Boolean, + readComment: Boolean, ): Any? { - val psiType = psiMethod.getResolvedReturnType() ?: return null - return when { - needInfer && (!duckTypeHelper!!.isQualified( - psiType, - psiMethod - ) || PsiClassUtils.isInterface(psiType)) -> { - logger!!.info("try infer return type of method[" + PsiClassUtils.fullNameOfMethod(psiMethod) + "]") - methodReturnInferHelper!!.inferReturn(psiMethod) - } - - else -> psiClassHelper!!.getTypeObject( - psiType, psiMethod, JsonOption.NONE.or(readGetter, readSetter) - ) - } - } - - open fun returnJson( - needInfer: Boolean = false, readGetter: Boolean = true, - readSetter: Boolean = true, - ): String? { - val psiType = psiMethod.getResolvedReturnType() ?: return null - return when { - needInfer && (!duckTypeHelper!!.isQualified( - psiType, - psiMethod - ) || PsiClassUtils.isInterface(psiType)) -> { - logger!!.info("try infer return type of method[" + PsiClassUtils.fullNameOfMethod(psiMethod) + "]") - methodReturnInferHelper!!.inferReturn(psiMethod) - } - - else -> psiClassHelper!!.getTypeObject( - psiType, psiMethod, JsonOption.NONE.or(readGetter, readSetter) - ) - }?.let { RequestUtils.parseRawBody(it) } - } - - override fun toString(): String { - return containingClass().name() + "#" + psiMethod.name - } - } - - @ScriptTypeName("method") - inner class ScriptExplicitMethodContext(private val explicitMethod: ExplicitMethod) : - ScriptPsiMethodContext(explicitMethod.psi()) { - - override fun returnType(): ScriptClassContext? { - return explicitMethod.getReturnType()?.let { ScriptDuckTypeContext(it) } + return psiClassHelper.getTypeObject( + psiType, getResource()!!, JsonOption.NONE.or(readGetter, readSetter, readComment) + ) } - override fun returnObject(needInfer: Boolean, readGetter: Boolean, readSetter: Boolean): Any? { - val duckType = explicitMethod.getReturnType() ?: return null - return when { - needInfer && !duckTypeHelper!!.isQualified(duckType) -> { - logger!!.info("try infer return type of method[" + PsiClassUtils.fullNameOfMethod(psiMethod) + "]") - methodReturnInferHelper!!.inferReturn(psiMethod) - } - - else -> psiClassHelper!!.getTypeObject( - duckType, psiMethod, JsonOption.NONE.or(readGetter, readSetter) - ) + override fun superClass(): ScriptClassContext? { + val psiClass = getResource() as? PsiClass ?: return null + if (psiClass.isInterface || psiClass.isAnnotationType || psiClass.isEnum) { + return null } - } - - override fun returnJson( - needInfer: Boolean, readGetter: Boolean, - readSetter: Boolean, - ): String? { - val duckType = explicitMethod.getReturnType() ?: return null - return when { - needInfer && !duckTypeHelper!!.isQualified(duckType) -> { - logger!!.info("try infer return type of method[" + PsiClassUtils.fullNameOfMethod(psiMethod) + "]") - methodReturnInferHelper!!.inferReturn(psiMethod) - } - - else -> psiClassHelper!!.getTypeObject( - duckType, psiMethod, JsonOption.NONE.or(readGetter, readSetter) - ) - }?.let { RequestUtils.parseRawBody(it) } - } - - override fun args(): Array { - val parameters = explicitMethod.getParameters() - if (parameters.isEmpty()) return emptyArray() - return parameters.mapToTypedArray { ScriptExplicitParameterContext(it) } - } - - override fun argTypes(): Array { - val parameters = explicitMethod.getParameters() - if (parameters.isEmpty()) return emptyArray() - return parameters.mapToTypedArray { ScriptDuckTypeContext(it.getType()!!, it.psi()) } - } - - override fun containingClass(): ScriptPsiClassContext { - return ScriptExplicitClassContext(explicitMethod.containClass()) - } - - override fun defineClass(): ScriptPsiClassContext { - return ScriptExplicitClassContext(explicitMethod.defineClass()) - } - - @ScriptIgnore - override fun getCore(): Any { - return explicitMethod - } - } - - /** - * it.name:String - * it.type:class - * it.isVarArgs:Boolean - */ - @ScriptTypeName("arg") - open inner class ScriptPsiParameterContext(private val psiParameter: PsiParameter) : - BaseScriptRuleContext(psiParameter) { - override fun contextType(): String { - return "param" - } - - open fun type(): ScriptClassContext { - return ScriptPsiTypeContext(psiParameter.type) + extends()?.takeIf { it.isNotEmpty() }?.get(0)?.let { + return it + } + return psiClass.superClass?.let { ScriptPsiClassContext(it) } } /** - * Checks if the parameter accepts a variable number of arguments. + * Returns the list of classes that this class or interface extends. * - * @return true if the parameter is a vararg, false otherwise + * @return the extends list, or null for anonymous classes. */ - fun isVarArgs(): Boolean { - return psiParameter.isVarArgs - } - - override fun getName(): String? { - return actionContext.callInReadUI { psiParameter.name } - } - - @ScriptIgnore - override fun asPsiModifierListOwner(): PsiModifierListOwner? { - return psiParameter + override fun extends(): Array? { + return explicitClassContext?.extends() } /** - * Returns the method which declare the param. - * May be null + * Returns the list of interfaces that this class implements. + * + * @return the implements list, or null for anonymous classes */ - open fun method(): ScriptPsiMethodContext? { - return declaration() as? ScriptPsiMethodContext + override fun implements(): Array? { + return explicitClassContext?.implements() } - /** - * Returns the element which declare the param. - */ - open fun declaration(): RuleContext { - return psiParameter.declarationScope.let { - contextOf(it, it) - } + override fun mavenId(): MavenIdData? { + val psiClass = getResource() as? PsiClass ?: return null + return actionContext.callInReadUI { MavenHelper.getMavenId(psiClass) } } override fun toString(): String { return name() } - } - - @ScriptTypeName("arg") - inner class ScriptExplicitParameterContext(private val explicitParam: ExplicitParameter) : - ScriptPsiParameterContext(explicitParam.psi()) { - - override fun type(): ScriptClassContext { - return explicitParam.getType()?.let { ScriptDuckTypeContext(it) } ?: super.type() - } - - @ScriptIgnore - override fun getCore(): Any { - return explicitParam - } - - override fun method(): ScriptPsiMethodContext { - return ScriptExplicitMethodContext(explicitParam.containMethod()) - } - - override fun declaration(): RuleContext { - return ScriptExplicitMethodContext(explicitParam.containMethod()) - } - } - - @ScriptTypeName("class") - abstract inner class ScriptClassContext : BaseScriptRuleContext { - constructor(psiElement: PsiElement) : super(psiElement) - constructor() : super() - - abstract fun methods(): Array - - abstract fun fields(): Array - - abstract fun isExtend(superClass: String): Boolean - - abstract fun isMap(): Boolean - - abstract fun isCollection(): Boolean - - abstract fun isArray(): Boolean - - /** - * Returns whether this class is a primitive - */ - abstract fun isPrimitive(): Boolean - - /** - * Returns whether this class is a primitive wrapper - */ - abstract fun isPrimitiveWrapper(): Boolean - - /** - * Returns whether the given {@code type} is a primitive or primitive wrapper - * or {@code String}、{@code Object} - */ - abstract fun isNormalType(): Boolean - - /** - * Checks if the class is an interface. - * - * @return true if the class is an interface, false otherwise. - */ - abstract fun isInterface(): Boolean - - /** - * Checks if the class is an annotation type. - * - * @return true if the class is an annotation type, false otherwise - */ - abstract fun isAnnotationType(): Boolean - - /** - * Checks if the class is an enumeration. - * - * @return true if the class is an enumeration, false otherwise. - */ - abstract fun isEnum(): Boolean - - abstract fun fieldCnt(): Int - - abstract fun methodCnt(): Int - - fun toObject(): Any? { - return toObject(readGetter = true, readSetter = true, readComment = false) - } - - abstract fun toObject( - readGetter: Boolean, readSetter: Boolean, - readComment: Boolean, - ): Any? - - fun toJson(): String? { - return toJson(readGetter = true, readSetter = true) - } - - fun toJson(readGetter: Boolean, readSetter: Boolean): String? { - return toObject(readGetter, readSetter, false) - ?.let { RequestUtils.parseRawBody(it) } - } - fun toJson5(): String? { - return toJson5(false, false) - } - - fun toJson5(readGetter: Boolean, readSetter: Boolean): String? { - return toObject(readGetter, readSetter, true)?.let { - actionContext.instance(Json5Formatter::class).format(it) - } - } - - /** - * Returns the base class of this class. - * If this class represents either the - * [Object] class, an interface, a primitive type, or void, then - * null is returned. - * {class A extend B} -> B - * {class A} -> java.lang.Object - * {class A implement IA} -> java.lang.Object - * {class A extend B implement IA} -> B - * {interface IA} -> null - * {interface IA extend IB} -> null - * - * @return the base class. May return null when jdk is not configured, so no java.lang.Object is found, - * or for java.lang.Object itself - */ - abstract fun superClass(): ScriptClassContext? - - /** - * Returns the list of classes that this class or interface extends. - * - * @return the extends list, or null for anonymous classes. - */ - abstract fun extends(): Array? + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ScriptClassContext) return false + return toString() == other.toString() + } - /** - * Returns the list of interfaces that this class implements. - * - * @return the implements list, or null for anonymous classes - */ - abstract fun implements(): Array? + override fun hashCode(): Int { + return toString().hashCode() + } + } - abstract fun mavenId(): MavenIdData? + fun ScriptDuckTypeContext(duckType: DuckType): ScriptDuckTypeContext { + return ScriptDuckTypeContext(duckType, (duckType as? SingleDuckType)?.psiClass()) } /** @@ -1088,7 +928,14 @@ abstract class ScriptRuleParser : AbstractRuleParser() { * @see ScriptPsiClassContext */ @ScriptTypeName("class") - inner class ScriptPsiTypeContext(private val psiType: PsiType) : ScriptClassContext() { + inner class ScriptDuckTypeContext( + private val duckType: DuckType, + psiElement: PsiElement? + ) : ScriptClassContext(psiElement) { + + private val explicitClassContext by lazy { + duckType.explicit()?.let { ScriptExplicitClassContext(it) } + } override fun contextType(): String { return "class" @@ -1096,13 +943,16 @@ abstract class ScriptRuleParser : AbstractRuleParser() { @ScriptIgnore override fun getPsiContext(): PsiElement? { - return getResource() ?: jvmClassHelper!!.resolveClassInType(psiType) + return getResource() } - private var duckType: DuckType? = null + @ScriptIgnore + override fun getCore(): Any { + return duckType + } - override fun getName(): String? { - return duckType?.canonicalText() + override fun getName(): String { + return duckType.canonicalText() } override fun getSimpleName(): String? { @@ -1120,27 +970,26 @@ abstract class ScriptRuleParser : AbstractRuleParser() { } override fun methods(): Array { - return (getResource() as? PsiClass)?.let { psiElement -> - return@let jvmClassHelper!!.getAllMethods(psiElement).mapToTypedArray { ScriptPsiMethodContext(it) } - } ?: emptyArray() + return explicitClassContext?.methods() ?: emptyArray() } override fun fields(): Array { - return (getResource() as? PsiClass)?.let { psiElement -> - return@let jvmClassHelper!!.getAllFields(psiElement).mapToTypedArray { ScriptPsiFieldContext(it) } - } ?: emptyArray() + return explicitClassContext?.fields() ?: emptyArray() } + /** + * Checks if the current class extends the specified superclass. + */ override fun isExtend(superClass: String): Boolean { - return jvmClassHelper!!.isInheritor(psiType, superClass) + return jvmClassHelper.isInheritor(duckType, superClass) } override fun isMap(): Boolean { - return jvmClassHelper!!.isMap(psiType) + return jvmClassHelper.isMap(duckType) } override fun isCollection(): Boolean { - return jvmClassHelper!!.isCollection(psiType) + return jvmClassHelper.isCollection(duckType) } override fun isArray(): Boolean { @@ -1152,44 +1001,44 @@ abstract class ScriptRuleParser : AbstractRuleParser() { * or {@code String}、{@code Object} */ override fun isNormalType(): Boolean { - return jvmClassHelper!!.isNormalType(name()) + return jvmClassHelper.isNormalType(name()) } override fun isInterface(): Boolean { - return (getResource() as? PsiClass)?.isInterface ?: false + return duckType is SingleDuckType && duckType.psiClass().isInterface } override fun isAnnotationType(): Boolean { - return (getResource() as? PsiClass)?.isAnnotationType ?: false + return duckType is SingleDuckType && duckType.psiClass().isAnnotationType } override fun isEnum(): Boolean { - return (getResource() as? PsiClass)?.isEnum ?: false + return duckType is SingleDuckType && duckType.psiClass().isEnum } /** * Returns whether this class is a primitive */ override fun isPrimitive(): Boolean { - return jvmClassHelper!!.isPrimitive(name()) + return jvmClassHelper.isPrimitive(name()) } /** * Returns whether this class is a primitive wrapper */ override fun isPrimitiveWrapper(): Boolean { - return jvmClassHelper!!.isPrimitiveWrapper(name()) + return jvmClassHelper.isPrimitiveWrapper(name()) } override fun fieldCnt(): Int { return (getResource() as? PsiClass)?.let { psiElement -> - return@let jvmClassHelper!!.getAllFields(psiElement).mapToTypedArray { ScriptPsiFieldContext(it) } + return@let jvmClassHelper.getAllFields(psiElement).mapToTypedArray { ScriptPsiFieldContext(it) } }?.size ?: 0 } override fun methodCnt(): Int { return (getResource() as? PsiClass)?.let { psiElement -> - return@let jvmClassHelper!!.getAllMethods(psiElement).mapToTypedArray { ScriptPsiMethodContext(it) } + return@let jvmClassHelper.getAllMethods(psiElement).mapToTypedArray { ScriptPsiMethodContext(it) } }?.size ?: 0 } @@ -1198,13 +1047,19 @@ abstract class ScriptRuleParser : AbstractRuleParser() { readSetter: Boolean, readComment: Boolean, ): Any? { - return psiClassHelper!!.getTypeObject( - psiType, getResource()!!, JsonOption.NONE.or(readGetter, readSetter, readComment) + val resource: PsiElement = getResource() ?: return null + return psiClassHelper.getTypeObject( + duckType, resource, JsonOption.NONE.or( + readGetter, + readSetter, + readComment + ) ) } override fun superClass(): ScriptClassContext? { - val psiClass = getResource() as? PsiClass ?: return null + val duckType = this.duckType as? SingleDuckType ?: return null + val psiClass = duckType.psiClass() if (psiClass.isInterface || psiClass.isAnnotationType || psiClass.isEnum) { return null } @@ -1214,16 +1069,17 @@ abstract class ScriptRuleParser : AbstractRuleParser() { return psiClass.superClass?.let { ScriptPsiClassContext(it) } } + override fun toString(): String { + return name() + } + /** * Returns the list of classes that this class or interface extends. * * @return the extends list, or null for anonymous classes. */ override fun extends(): Array? { - val duckType = duckTypeHelper!!.resolve(psiType, getPsiContext()!!) as? SingleDuckType ?: return null - return duckTypeHelper.explicit(duckType).extends()?.mapToTypedArray { - ScriptExplicitClassContext(it) - } + return explicitClassContext?.extends() } /** @@ -1232,10 +1088,7 @@ abstract class ScriptRuleParser : AbstractRuleParser() { * @return the implements list, or null for anonymous classes */ override fun implements(): Array? { - val duckType = duckTypeHelper!!.resolve(psiType, getPsiContext()!!) as? SingleDuckType ?: return null - return duckTypeHelper.explicit(duckType).implements()?.mapToTypedArray { - ScriptExplicitClassContext(it) - } + return explicitClassContext?.implements() } override fun mavenId(): MavenIdData? { @@ -1243,229 +1096,421 @@ abstract class ScriptRuleParser : AbstractRuleParser() { return actionContext.callInReadUI { MavenHelper.getMavenId(psiClass) } } - override fun toString(): String { - return name() + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ScriptClassContext) return false + return toString() == other.toString() + } + + override fun hashCode(): Int { + return toString().hashCode() + } + } + + /** + * it.type:class + * it.containingClass:class + * it.jsonName:String + */ + @ScriptTypeName("field") + open inner class ScriptPsiFieldContext(private val psiField: PsiField) : BaseScriptRuleContext(psiField) { + override fun contextType(): String { + return "field" + } + + open fun type(): ScriptClassContext { + return ScriptPsiTypeContext(psiField.type) + } + + @ScriptIgnore + override fun asPsiModifierListOwner(): PsiModifierListOwner? { + return psiField + } + + override fun getName(): String? { + return psiField.name + } + + /** + * Returns the class containing the member. + * + * @return the containing class. + */ + open fun containingClass(): ScriptPsiClassContext { + return ScriptPsiClassContext(psiField.containingClass!!) + } + + /** + * Returns the class define the member. + * + * @return the defining class. + */ + open fun defineClass(): ScriptPsiClassContext { + return containingClass() + } + + /** + * attention:it should not be used in [json.rule.field.name] + */ + fun jsonName(): String { + return psiClassHelper.getJsonFieldName(psiField) + } + + fun jsonType(): ScriptPsiTypeContext { + return ScriptPsiTypeContext(classRuleConfig.tryConvert(psiField.type, psiField)) + } + + fun isEnumField(): Boolean { + return psiField is PsiEnumConstant + } + + fun asEnumField(): ScriptPsiEnumConstantContext? { + return (psiField as? PsiEnumConstant)?.let { ScriptPsiEnumConstantContext(it) } + } + + override fun toString(): String { + return containingClass().name() + "#" + psiField.name + } + } + + @ScriptTypeName("field") + inner class ScriptExplicitFieldContext(private val explicitField: ExplicitField) : + ScriptPsiFieldContext(explicitField.psi()) { + + override fun containingClass(): ScriptPsiClassContext { + return ScriptExplicitClassContext(explicitField.containClass()) + } + + override fun defineClass(): ScriptPsiClassContext { + return ScriptExplicitClassContext(explicitField.defineClass()) + } + + override fun type(): ScriptClassContext { + return ScriptDuckTypeContext(explicitField.getType(), explicitField.psi()) + } + + @ScriptIgnore + override fun getCore(): Any { + return explicitField + } + } + + @ScriptTypeName("enumField") + inner class ScriptPsiEnumConstantContext(private val psiEnumConstant: PsiEnumConstant) : + BaseScriptRuleContext(psiEnumConstant) { + + override fun getName(): String { + return psiEnumConstant.name + } + + fun ordinal(): Int { + return actionContext.callInReadUI { + psiEnumConstant.containingClass?.fields?.indexOf(psiEnumConstant) + } ?: 0 + } + + private fun getEnumFields(): Map { + return actionContext.notReentrant("enumFields") { + actionContext.callInReadUI { + actionContext.instance(PsiResolver::class).resolveEnumFields( + ordinal(), psiEnumConstant + ) + } + } ?: emptyMap() + + } + + fun getParams(): Map { + return getEnumFields().getSub("params") ?: emptyMap() + } + + fun getParam(name: String): Any? { + return getParams()[name] + } + } + + /** + * it.returnType:class + * it.isVarArgs:Boolean + * it.args:arg[] + * it.argTypes:class[] + * it.argCnt:int + * it.containingClass:class + */ + @ScriptTypeName("method") + open inner class ScriptPsiMethodContext(protected val psiMethod: PsiMethod) : BaseScriptRuleContext(psiMethod) { + + override fun contextType(): String { + return "method" + } + + /** + * Returns the return type of the method. + * + * @return the method return type, or null if the method is a constructor. + */ + open fun returnType(): ScriptClassContext? { + return psiMethod.getResolvedReturnType()?.let { ScriptPsiTypeContext(it) } + } + + /** + * Checks if the method accepts a variable number of arguments. + * + * @return true if the method is varargs, false otherwise. + */ + fun isVarArgs(): Boolean { + return psiMethod.isVarArgs + } + + /** + * Returns the array of method parameters + */ + open fun args(): Array { + return psiMethod.parameterList.parameters.mapToTypedArray { ScriptPsiParameterContext(it) } + } + + /** + * Returns the array of method parameters type + */ + open fun argTypes(): Array { + return psiMethod.parameterList.parameters.mapToTypedArray { ScriptPsiTypeContext(it.type) } + } + + /** + * Returns the number of method parameters + */ + fun argCnt(): Int { + return psiMethod.parameterList.parametersCount + } + + /** + * Returns the class containing the member. + * + * @return the containing class. + */ + open fun containingClass(): ScriptPsiClassContext { + return ScriptPsiClassContext(psiMethod.containingClass!!) + } + + /** + * Returns the class define the member. + * + * @return the defining class. + */ + open fun defineClass(): ScriptPsiClassContext { + return containingClass() + } + + @ScriptIgnore + override fun asPsiModifierListOwner(): PsiModifierListOwner? { + return psiMethod } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is ScriptClassContext) return false - return toString() == other.toString() + override fun getName(): String? { + return actionContext.callInReadUI { psiMethod.name } } - override fun hashCode(): Int { - return toString().hashCode() + fun jsonName(): String { + return psiClassHelper.getJsonFieldName(psiMethod) } - init { - duckType = duckTypeHelper!!.ensureType(psiType) - if (duckType != null && duckType is SingleDuckType) { - this.psiElement = (duckType as SingleDuckType).psiClass() - } + fun type(): ScriptClassContext? { + return returnType() } - } - /** - * it.methods():method[] - * it.isExtend(""):Boolean - * it.isSuper(""):Boolean - * it.isMap():Boolean - * it.isCollection():Boolean - * it.isArray():Boolean - * @see ScriptPsiClassContext - */ - @ScriptTypeName("class") - inner class ScriptDuckTypeContext : ScriptClassContext { + fun jsonType(): ScriptPsiTypeContext? { + return psiMethod.returnType?.let { classRuleConfig.tryConvert(it, psiMethod) } + ?.let { ScriptPsiTypeContext(it) } + } - private val duckType: DuckType + open fun returnObject( + needInfer: Boolean = false, readGetter: Boolean = true, + readSetter: Boolean = true, + ): Any? { + val psiType = psiMethod.getResolvedReturnType() ?: return null + return when { + needInfer && (!duckTypeHelper.isQualified( + psiType, + psiMethod + ) || PsiClassUtils.isInterface(psiType)) -> { + logger.info("try infer return type of method[" + PsiClassUtils.fullNameOfMethod(psiMethod) + "]") + methodReturnInferHelper.inferReturn(psiMethod) + } - constructor(duckType: DuckType) : super() { - this.duckType = duckType - if (duckType is SingleDuckType) { - this.psiElement = duckType.psiClass() + else -> psiClassHelper.getTypeObject( + psiType, psiMethod, JsonOption.NONE.or(readGetter, readSetter) + ) } } - constructor(duckType: DuckType, psiElement: PsiElement) : super(psiElement) { - this.duckType = duckType - } + open fun returnJson( + needInfer: Boolean = false, readGetter: Boolean = true, + readSetter: Boolean = true, + ): String? { + val psiType = psiMethod.getResolvedReturnType() ?: return null + return when { + needInfer && (!duckTypeHelper.isQualified( + psiType, + psiMethod + ) || PsiClassUtils.isInterface(psiType)) -> { + logger.info("try infer return type of method[" + PsiClassUtils.fullNameOfMethod(psiMethod) + "]") + methodReturnInferHelper.inferReturn(psiMethod) + } - override fun contextType(): String { - return "class" + else -> psiClassHelper.getTypeObject( + psiType, psiMethod, JsonOption.NONE.or(readGetter, readSetter) + ) + }?.let { RequestUtils.parseRawBody(it) } } - @ScriptIgnore - override fun getPsiContext(): PsiElement? { - return getResource() + override fun toString(): String { + return containingClass().name() + "#" + psiMethod.name } + } - @ScriptIgnore - override fun getCore(): Any { - return duckType - } + @ScriptTypeName("method") + inner class ScriptExplicitMethodContext(private val explicitMethod: ExplicitMethod) : + ScriptPsiMethodContext(explicitMethod.psi()) { - override fun getName(): String { - return duckType.canonicalText() + override fun returnType(): ScriptClassContext? { + return explicitMethod.getReturnType()?.let { ScriptDuckTypeContext(it) } } - override fun getSimpleName(): String? { - return getDuckTypeSimpleName(duckType) - } + override fun returnObject(needInfer: Boolean, readGetter: Boolean, readSetter: Boolean): Any? { + val duckType = explicitMethod.getReturnType() ?: return null + return when { + needInfer && !duckTypeHelper.isQualified(duckType) -> { + logger.info("try infer return type of method[" + PsiClassUtils.fullNameOfMethod(psiMethod) + "]") + methodReturnInferHelper.inferReturn(psiMethod) + } - private fun getDuckTypeSimpleName(duckType: DuckType?): String? { - return when (duckType) { - null -> null - is SingleUnresolvedDuckType -> duckType.psiType().presentableText - is SingleDuckType -> duckType.psiClass().name - is ArrayDuckType -> getDuckTypeSimpleName(duckType.componentType()) + "[]" - else -> duckType.toString() + else -> psiClassHelper.getTypeObject( + duckType, psiMethod, JsonOption.NONE.or(readGetter, readSetter) + ) } } - override fun methods(): Array { - return (getResource() as? PsiClass)?.let { psiElement -> - return@let jvmClassHelper!!.getAllMethods(psiElement).mapToTypedArray { ScriptPsiMethodContext(it) } - } ?: emptyArray() - } - - override fun fields(): Array { - return (getResource() as? PsiClass)?.let { psiElement -> - return@let jvmClassHelper!!.getAllFields(psiElement).mapToTypedArray { ScriptPsiFieldContext(it) } - } ?: emptyArray() - } - - override fun isExtend(superClass: String): Boolean { - return jvmClassHelper!!.isInheritor(duckType, superClass) - } + override fun returnJson( + needInfer: Boolean, readGetter: Boolean, + readSetter: Boolean, + ): String? { + val duckType = explicitMethod.getReturnType() ?: return null + return when { + needInfer && !duckTypeHelper.isQualified(duckType) -> { + logger.info("try infer return type of method[" + PsiClassUtils.fullNameOfMethod(psiMethod) + "]") + methodReturnInferHelper.inferReturn(psiMethod) + } - override fun isMap(): Boolean { - return jvmClassHelper!!.isMap(duckType) + else -> psiClassHelper.getTypeObject( + duckType, psiMethod, JsonOption.NONE.or(readGetter, readSetter) + ) + }?.let { RequestUtils.parseRawBody(it) } } - override fun isCollection(): Boolean { - return jvmClassHelper!!.isCollection(duckType) + override fun args(): Array { + val parameters = explicitMethod.getParameters() + if (parameters.isEmpty()) return emptyArray() + return parameters.mapToTypedArray { ScriptExplicitParameterContext(it) } } - override fun isArray(): Boolean { - return duckType is ArrayDuckType + override fun argTypes(): Array { + val parameters = explicitMethod.getParameters() + if (parameters.isEmpty()) return emptyArray() + return parameters.mapToTypedArray { ScriptDuckTypeContext(it.getType()!!, it.psi()) } } - /** - * Returns whether the given {@code type} is a primitive or primitive wrapper - * or {@code String}、{@code Object} - */ - override fun isNormalType(): Boolean { - return jvmClassHelper!!.isNormalType(name()) + override fun containingClass(): ScriptPsiClassContext { + return ScriptExplicitClassContext(explicitMethod.containClass()) } - override fun isInterface(): Boolean { - return duckType is SingleDuckType && duckType.psiClass().isInterface + override fun defineClass(): ScriptPsiClassContext { + return ScriptExplicitClassContext(explicitMethod.defineClass()) } - override fun isAnnotationType(): Boolean { - return duckType is SingleDuckType && duckType.psiClass().isAnnotationType + @ScriptIgnore + override fun getCore(): Any { + return explicitMethod } + } - override fun isEnum(): Boolean { - return duckType is SingleDuckType && duckType.psiClass().isEnum + /** + * it.name:String + * it.type:class + * it.isVarArgs:Boolean + */ + @ScriptTypeName("arg") + open inner class ScriptPsiParameterContext(private val psiParameter: PsiParameter) : + BaseScriptRuleContext(psiParameter) { + override fun contextType(): String { + return "param" } - /** - * Returns whether this class is a primitive - */ - override fun isPrimitive(): Boolean { - return jvmClassHelper!!.isPrimitive(name()) + open fun type(): ScriptClassContext { + return ScriptPsiTypeContext(psiParameter.type) } /** - * Returns whether this class is a primitive wrapper + * Checks if the parameter accepts a variable number of arguments. + * + * @return true if the parameter is a vararg, false otherwise */ - override fun isPrimitiveWrapper(): Boolean { - return jvmClassHelper!!.isPrimitiveWrapper(name()) + fun isVarArgs(): Boolean { + return psiParameter.isVarArgs } - override fun fieldCnt(): Int { - return (getResource() as? PsiClass)?.let { psiElement -> - return@let jvmClassHelper!!.getAllFields(psiElement).mapToTypedArray { ScriptPsiFieldContext(it) } - }?.size ?: 0 + override fun getName(): String? { + return actionContext.callInReadUI { psiParameter.name } } - override fun methodCnt(): Int { - return (getResource() as? PsiClass)?.let { psiElement -> - return@let jvmClassHelper!!.getAllMethods(psiElement).mapToTypedArray { ScriptPsiMethodContext(it) } - }?.size ?: 0 + @ScriptIgnore + override fun asPsiModifierListOwner(): PsiModifierListOwner? { + return psiParameter } - override fun toObject( - readGetter: Boolean, - readSetter: Boolean, - readComment: Boolean, - ): Any? { - val resource: PsiElement = getResource() ?: return null - return psiClassHelper!!.getTypeObject( - duckType, resource, JsonOption.NONE.or( - readGetter, - readSetter, - readComment - ) - ) + /** + * Returns the method which declare the param. + * May be null + */ + open fun method(): ScriptPsiMethodContext? { + return declaration() as? ScriptPsiMethodContext } - override fun superClass(): ScriptClassContext? { - val duckType = this.duckType as? SingleDuckType ?: return null - val psiClass = duckType.psiClass() - if (psiClass.isInterface || psiClass.isAnnotationType || psiClass.isEnum) { - return null - } - extends()?.takeIf { it.isNotEmpty() }?.get(0)?.let { - return it + /** + * Returns the element which declare the param. + */ + open fun declaration(): RuleContext { + return psiParameter.declarationScope.let { + contextOf(it, it) } - return psiClass.superClass?.let { ScriptPsiClassContext(it) } } override fun toString(): String { return name() } + } - /** - * Returns the list of classes that this class or interface extends. - * - * @return the extends list, or null for anonymous classes. - */ - override fun extends(): Array? { - val duckType = this.duckType as? SingleDuckType ?: return null - return duckTypeHelper!!.explicit(duckType).extends()?.mapToTypedArray { - ScriptExplicitClassContext(it) - } - } + @ScriptTypeName("arg") + inner class ScriptExplicitParameterContext(private val explicitParam: ExplicitParameter) : + ScriptPsiParameterContext(explicitParam.psi()) { - /** - * Returns the list of interfaces that this class implements. - * - * @return the implements list, or null for anonymous classes - */ - override fun implements(): Array? { - val duckType = this.duckType as? SingleDuckType ?: return null - return duckTypeHelper!!.explicit(duckType).implements()?.mapToTypedArray { - ScriptExplicitClassContext(it) - } + override fun type(): ScriptClassContext { + return explicitParam.getType()?.let { ScriptDuckTypeContext(it) } ?: super.type() } - override fun mavenId(): MavenIdData? { - val psiClass = getResource() as? PsiClass ?: return null - return actionContext.callInReadUI { MavenHelper.getMavenId(psiClass) } + @ScriptIgnore + override fun getCore(): Any { + return explicitParam } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is ScriptClassContext) return false - return toString() == other.toString() + override fun method(): ScriptPsiMethodContext { + return ScriptExplicitMethodContext(explicitParam.containMethod()) } - override fun hashCode(): Int { - return toString().hashCode() + override fun declaration(): RuleContext { + return ScriptExplicitMethodContext(explicitParam.containMethod()) } } + } private fun Int.or(