From e949662e7c335ae55c4945220290836239afe16e Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Wed, 25 Jun 2025 19:05:35 +0800 Subject: [PATCH] Initial commit --- .editorconfig | 35 + .gitignore | 110 + .idea/.gitignore | 8 + .../includes/copyright-name.template | 1 + .../includes/license-content.template | 13 + .../open-source-license-header.template | 13 + .../includes/project-description.template | 1 + .../includes/project-name.template | 1 + .../includes/project-url.template | 1 + .../internal/Kotlin Annotation.kt | 6 + .idea/fileTemplates/internal/Kotlin Class.kt | 7 + .../internal/Kotlin Data Class.kt | 6 + .idea/fileTemplates/internal/Kotlin Enum.kt | 7 + .idea/fileTemplates/internal/Kotlin File.kt | 5 + .../internal/Kotlin Interface.kt | 7 + .idea/fileTemplates/internal/Kotlin Object.kt | 7 + .idea/icon.svg | 11 + .idea/inspectionProfiles/Project_Default.xml | 10 + .idea/kotlinc.xml | 6 + .idea/ktlint-plugin.xml | 7 + .idea/vcs.xml | 6 + LICENSE | 202 ++ README-zh-CN.md | 72 + README.md | 75 + build.gradle.kts | 57 + docs-source/.gitignore | 4 + docs-source/.vscode/settings.json | 3 + docs-source/build-dokka.sh | 2 + docs-source/package.json | 17 + docs-source/src/.vuepress/config.ts | 64 + docs-source/src/.vuepress/configs/template.ts | 148 ++ docs-source/src/.vuepress/configs/utils.ts | 39 + .../src/.vuepress/public/images/logo.svg | 11 + docs-source/src/.vuepress/styles/index.scss | 179 ++ docs-source/src/en/about/about.md | 27 + docs-source/src/en/about/changelog.md | 29 + docs-source/src/en/about/contacts.md | 16 + docs-source/src/en/about/future.md | 114 + docs-source/src/en/config/migration.md | 266 +++ .../src/en/config/processor-resolvers.md | 37 + docs-source/src/en/config/r8-proguard.md | 6 + docs-source/src/en/guide/home.md | 46 + docs-source/src/en/guide/quick-start.md | 97 + docs-source/src/en/index.md | 38 + docs-source/src/en/library/kavaref-core.md | 790 +++++++ .../src/en/library/kavaref-extension.md | 315 +++ docs-source/src/index.md | 17 + docs-source/src/zh-cn/about/about.md | 27 + docs-source/src/zh-cn/about/changelog.md | 21 + docs-source/src/zh-cn/about/contacts.md | 15 + docs-source/src/zh-cn/about/future.md | 113 + docs-source/src/zh-cn/config/migration.md | 243 ++ .../src/zh-cn/config/processor-resolvers.md | 38 + docs-source/src/zh-cn/config/r8-proguard.md | 5 + docs-source/src/zh-cn/guide/home.md | 36 + docs-source/src/zh-cn/guide/quick-start.md | 95 + docs-source/src/zh-cn/index.md | 38 + docs-source/src/zh-cn/library/kavaref-core.md | 768 +++++++ .../src/zh-cn/library/kavaref-extension.md | 295 +++ docs-source/yarn.lock | 2004 +++++++++++++++++ gradle.properties | 25 + .../sweet-dependency-config.yaml | 28 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 234 ++ gradlew.bat | 89 + img-src/icon.svg | 11 + kavaref-android-stub/build.gradle.kts | 10 + .../src/main/java/android/util/Log.java | 119 + kavaref-core/build.gradle.kts | 34 + .../kotlin/com/highcapable/kavaref/KavaRef.kt | 433 ++++ .../kavaref/condition/ConstructorCondition.kt | 87 + .../kavaref/condition/FieldCondition.kt | 153 ++ .../kavaref/condition/MethodCondition.kt | 181 ++ .../condition/base/ExecutableCondition.kt | 433 ++++ .../kavaref/condition/base/MemberCondition.kt | 307 +++ .../condition/extension/MemberCondition.kt | 35 + .../condition/matcher/ClassTypeMatcher.kt | 36 + .../matcher/GenericArrayTypeMatcher.kt | 43 + .../matcher/ParameterizedTypeMatcher.kt | 48 + .../condition/matcher/TypeVariableMatcher.kt | 38 + .../condition/matcher/WildcardTypeMatcher.kt | 48 + .../condition/matcher/base/TypeMatcher.kt | 37 + .../matcher/extension/TypeMatcher.kt | 89 + .../kavaref/condition/type/Modifiers.kt | 62 + .../kavaref/condition/type/VagueType.kt | 69 + .../kavaref/resolver/ConstructorResolver.kt | 84 + .../kavaref/resolver/FieldResolver.kt | 100 + .../kavaref/resolver/MethodResolver.kt | 81 + .../resolver/base/InstanceAwareResolver.kt | 65 + .../kavaref/resolver/base/MemberResolver.kt | 39 + .../resolver/processor/MemberProcessor.kt | 518 +++++ .../kavaref/runtime/KavaRefRuntime.kt | 209 ++ kavaref-extension/build.gradle.kts | 26 + .../kavaref/extension/JavaArrayClass.kt | 66 + .../kavaref/extension/JavaClass.kt | 671 ++++++ .../kavaref/extension/JavaMember.kt | 138 ++ .../highcapable/kavaref/extension/JavaType.kt | 106 + .../kavaref/extension/JavaWrapperClasses.kt | 52 + samples/kavaref-demo/build.gradle.kts | 28 + .../com/highcapable/kavaref/demo/Main.java | 68 + .../highcapable/kavaref/demo/test/Test.java | 44 + .../com/highcapable/kavaref/demo/Main.kt | 80 + settings.gradle.kts | 30 + 104 files changed, 11697 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/fileTemplates/includes/copyright-name.template create mode 100644 .idea/fileTemplates/includes/license-content.template create mode 100644 .idea/fileTemplates/includes/open-source-license-header.template create mode 100644 .idea/fileTemplates/includes/project-description.template create mode 100644 .idea/fileTemplates/includes/project-name.template create mode 100644 .idea/fileTemplates/includes/project-url.template create mode 100644 .idea/fileTemplates/internal/Kotlin Annotation.kt create mode 100644 .idea/fileTemplates/internal/Kotlin Class.kt create mode 100644 .idea/fileTemplates/internal/Kotlin Data Class.kt create mode 100644 .idea/fileTemplates/internal/Kotlin Enum.kt create mode 100644 .idea/fileTemplates/internal/Kotlin File.kt create mode 100644 .idea/fileTemplates/internal/Kotlin Interface.kt create mode 100644 .idea/fileTemplates/internal/Kotlin Object.kt create mode 100644 .idea/icon.svg create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/ktlint-plugin.xml create mode 100644 .idea/vcs.xml create mode 100644 LICENSE create mode 100644 README-zh-CN.md create mode 100644 README.md create mode 100644 build.gradle.kts create mode 100644 docs-source/.gitignore create mode 100644 docs-source/.vscode/settings.json create mode 100755 docs-source/build-dokka.sh create mode 100644 docs-source/package.json create mode 100644 docs-source/src/.vuepress/config.ts create mode 100644 docs-source/src/.vuepress/configs/template.ts create mode 100644 docs-source/src/.vuepress/configs/utils.ts create mode 100644 docs-source/src/.vuepress/public/images/logo.svg create mode 100644 docs-source/src/.vuepress/styles/index.scss create mode 100644 docs-source/src/en/about/about.md create mode 100644 docs-source/src/en/about/changelog.md create mode 100644 docs-source/src/en/about/contacts.md create mode 100644 docs-source/src/en/about/future.md create mode 100644 docs-source/src/en/config/migration.md create mode 100644 docs-source/src/en/config/processor-resolvers.md create mode 100644 docs-source/src/en/config/r8-proguard.md create mode 100644 docs-source/src/en/guide/home.md create mode 100644 docs-source/src/en/guide/quick-start.md create mode 100644 docs-source/src/en/index.md create mode 100644 docs-source/src/en/library/kavaref-core.md create mode 100644 docs-source/src/en/library/kavaref-extension.md create mode 100644 docs-source/src/index.md create mode 100644 docs-source/src/zh-cn/about/about.md create mode 100644 docs-source/src/zh-cn/about/changelog.md create mode 100644 docs-source/src/zh-cn/about/contacts.md create mode 100644 docs-source/src/zh-cn/about/future.md create mode 100644 docs-source/src/zh-cn/config/migration.md create mode 100644 docs-source/src/zh-cn/config/processor-resolvers.md create mode 100644 docs-source/src/zh-cn/config/r8-proguard.md create mode 100644 docs-source/src/zh-cn/guide/home.md create mode 100644 docs-source/src/zh-cn/guide/quick-start.md create mode 100644 docs-source/src/zh-cn/index.md create mode 100644 docs-source/src/zh-cn/library/kavaref-core.md create mode 100644 docs-source/src/zh-cn/library/kavaref-extension.md create mode 100644 docs-source/yarn.lock create mode 100644 gradle.properties create mode 100644 gradle/sweet-dependency/sweet-dependency-config.yaml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 img-src/icon.svg create mode 100644 kavaref-android-stub/build.gradle.kts create mode 100644 kavaref-android-stub/src/main/java/android/util/Log.java create mode 100644 kavaref-core/build.gradle.kts create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/KavaRef.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/ConstructorCondition.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/FieldCondition.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/MethodCondition.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/base/ExecutableCondition.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/base/MemberCondition.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/extension/MemberCondition.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/ClassTypeMatcher.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/GenericArrayTypeMatcher.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/ParameterizedTypeMatcher.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/TypeVariableMatcher.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/WildcardTypeMatcher.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/base/TypeMatcher.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/extension/TypeMatcher.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/type/Modifiers.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/type/VagueType.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/ConstructorResolver.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/FieldResolver.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/MethodResolver.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/base/InstanceAwareResolver.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/base/MemberResolver.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/processor/MemberProcessor.kt create mode 100644 kavaref-core/src/main/kotlin/com/highcapable/kavaref/runtime/KavaRefRuntime.kt create mode 100644 kavaref-extension/build.gradle.kts create mode 100644 kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaArrayClass.kt create mode 100644 kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaClass.kt create mode 100644 kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaMember.kt create mode 100644 kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaType.kt create mode 100644 kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaWrapperClasses.kt create mode 100644 samples/kavaref-demo/build.gradle.kts create mode 100644 samples/kavaref-demo/src/main/java/com/highcapable/kavaref/demo/Main.java create mode 100644 samples/kavaref-demo/src/main/java/com/highcapable/kavaref/demo/test/Test.java create mode 100644 samples/kavaref-demo/src/main/kotlin/com/highcapable/kavaref/demo/Main.kt create mode 100644 settings.gradle.kts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d9b9b1a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,35 @@ +# noinspection EditorConfigKeyCorrectness +[{*.kt,*.kts}] +ktlint_standard_annotation = disabled +ktlint_standard_filename = disabled +ktlint_standard_wrapping = disabled +ktlint_standard_import-ordering = enabled +ktlint_standard_max-line-length = disabled +ktlint_standard_multiline-if-else = disabled +ktlint_standard_argument-list-wrapping = disabled +ktlint_standard_parameter-list-wrapping = disabled +ktlint_standard_trailing-comma-on-declaration-site = disabled +ktlint_function_signature_body_expression_wrapping = multiline +ktlint_standard_string-template-indent = disabled +ktlint_standard_function-signature = disabled +ktlint_standard_trailing-comma-on-call-site = disabled +ktlint_standard_multiline-expression-wrapping = disabled +ktlint_standard_no-empty-first-line-in-class-body = disabled +ktlint_standard_if-else-wrapping = disabled +ktlint_standard_if-else-bracing = disabled +ktlint_standard_statement-wrapping = disabled +ktlint_standard_blank-line-before-declaration = disabled +ktlint_standard_no-empty-file = disabled +ktlint_standard_property-naming = disabled +ktlint_standard_function-naming = disabled +ktlint_standard_chain-method-continuation = disabled +ktlint_standard_class-signature = disabled +ktlint_standard_condition-wrapping = disabled +ktlint_standard_class-signature = disabled +ktlint_standard_no-trailing-spaces = disabled +ktlint_standard_multiline-loop = disabled +ij_continuation_indent_size = 2 +indent_size = 4 +indent_style = space +insert_final_newline = false +max_line_length = 150 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e1d8a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,110 @@ +## Fully .gtignore for IntelliJ, Android Studio and Gradle based Java projects +## References: +## - https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +## - https://github.com/android/platform-samples/blob/main/.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +.idea/.name +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +.idea/caches +.idea/material_theme** +.idea/other.xml +*.iml +*.ipr + +# Kotlin +.kotlin + +# Misc +.idea/misc.xml + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# Android studio 3.1+ additional +.idea/deployment*.xml +.idea/assetWizardSettings.xml +.idea/androidTestResultsUserPreferences.xml + +# Android projects +**/local.properties +/captures +.externalNativeBuild +.cxx + +# Gradle projects +.gradle +build/ + +# Mkdocs temporary serving folder +docs-gen +site +*.bak +.idea/appInsightsSettings.xml + +# Mac OS +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/fileTemplates/includes/copyright-name.template b/.idea/fileTemplates/includes/copyright-name.template new file mode 100644 index 0000000..ad0a243 --- /dev/null +++ b/.idea/fileTemplates/includes/copyright-name.template @@ -0,0 +1 @@ +2019 HighCapable \ No newline at end of file diff --git a/.idea/fileTemplates/includes/license-content.template b/.idea/fileTemplates/includes/license-content.template new file mode 100644 index 0000000..68616e9 --- /dev/null +++ b/.idea/fileTemplates/includes/license-content.template @@ -0,0 +1,13 @@ + * Apache License Version 2.0 + * + * 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 + * + * https://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. \ No newline at end of file diff --git a/.idea/fileTemplates/includes/open-source-license-header.template b/.idea/fileTemplates/includes/open-source-license-header.template new file mode 100644 index 0000000..6f41ea1 --- /dev/null +++ b/.idea/fileTemplates/includes/open-source-license-header.template @@ -0,0 +1,13 @@ +/* + * #parse("project-name") - #parse("project-description") + + * Copyright (C) #parse("copyright-name") + + * #parse("project-url") + + * + #parse("license-content") + + * + * This file is created by $USER on $DATE. + */ \ No newline at end of file diff --git a/.idea/fileTemplates/includes/project-description.template b/.idea/fileTemplates/includes/project-description.template new file mode 100644 index 0000000..e784df0 --- /dev/null +++ b/.idea/fileTemplates/includes/project-description.template @@ -0,0 +1 @@ +A modernizing Java Reflection with Kotlin. \ No newline at end of file diff --git a/.idea/fileTemplates/includes/project-name.template b/.idea/fileTemplates/includes/project-name.template new file mode 100644 index 0000000..6077d37 --- /dev/null +++ b/.idea/fileTemplates/includes/project-name.template @@ -0,0 +1 @@ +KavaRef \ No newline at end of file diff --git a/.idea/fileTemplates/includes/project-url.template b/.idea/fileTemplates/includes/project-url.template new file mode 100644 index 0000000..04f63e4 --- /dev/null +++ b/.idea/fileTemplates/includes/project-url.template @@ -0,0 +1 @@ +https://github.com/HighCapable/KavaRef \ No newline at end of file diff --git a/.idea/fileTemplates/internal/Kotlin Annotation.kt b/.idea/fileTemplates/internal/Kotlin Annotation.kt new file mode 100644 index 0000000..0e2b92b --- /dev/null +++ b/.idea/fileTemplates/internal/Kotlin Annotation.kt @@ -0,0 +1,6 @@ +#parse("open-source-license-header") + +#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME} +#end + +annotation class ${NAME} diff --git a/.idea/fileTemplates/internal/Kotlin Class.kt b/.idea/fileTemplates/internal/Kotlin Class.kt new file mode 100644 index 0000000..4112dfb --- /dev/null +++ b/.idea/fileTemplates/internal/Kotlin Class.kt @@ -0,0 +1,7 @@ +#parse("open-source-license-header") + +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME} + +#end +class ${NAME} { +} \ No newline at end of file diff --git a/.idea/fileTemplates/internal/Kotlin Data Class.kt b/.idea/fileTemplates/internal/Kotlin Data Class.kt new file mode 100644 index 0000000..db701e7 --- /dev/null +++ b/.idea/fileTemplates/internal/Kotlin Data Class.kt @@ -0,0 +1,6 @@ +#parse("open-source-license-header") + +#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME} +#end + +data class ${NAME}() diff --git a/.idea/fileTemplates/internal/Kotlin Enum.kt b/.idea/fileTemplates/internal/Kotlin Enum.kt new file mode 100644 index 0000000..a1fa97d --- /dev/null +++ b/.idea/fileTemplates/internal/Kotlin Enum.kt @@ -0,0 +1,7 @@ +#parse("open-source-license-header") + +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME} + +#end +enum class ${NAME} { +} \ No newline at end of file diff --git a/.idea/fileTemplates/internal/Kotlin File.kt b/.idea/fileTemplates/internal/Kotlin File.kt new file mode 100644 index 0000000..321d3a5 --- /dev/null +++ b/.idea/fileTemplates/internal/Kotlin File.kt @@ -0,0 +1,5 @@ +#parse("open-source-license-header") + +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME} + +#end diff --git a/.idea/fileTemplates/internal/Kotlin Interface.kt b/.idea/fileTemplates/internal/Kotlin Interface.kt new file mode 100644 index 0000000..de3a20b --- /dev/null +++ b/.idea/fileTemplates/internal/Kotlin Interface.kt @@ -0,0 +1,7 @@ +#parse("open-source-license-header") + +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME} + +#end +interface ${NAME} { +} \ No newline at end of file diff --git a/.idea/fileTemplates/internal/Kotlin Object.kt b/.idea/fileTemplates/internal/Kotlin Object.kt new file mode 100644 index 0000000..e9475be --- /dev/null +++ b/.idea/fileTemplates/internal/Kotlin Object.kt @@ -0,0 +1,7 @@ +#parse("open-source-license-header") + +#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME} +#end + +object ${NAME} { +} diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 0000000..fe777cc --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..146ab09 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..1e16934 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/ktlint-plugin.xml b/.idea/ktlint-plugin.xml new file mode 100644 index 0000000..e1cf0b8 --- /dev/null +++ b/.idea/ktlint-plugin.xml @@ -0,0 +1,7 @@ + + + + MANUAL + DEFAULT + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..80ba5d5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright HighCapable [name of copyright owner] + + 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 + + https://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. diff --git a/README-zh-CN.md b/README-zh-CN.md new file mode 100644 index 0000000..c6cf295 --- /dev/null +++ b/README-zh-CN.md @@ -0,0 +1,72 @@ +# KavaRef + +[![GitHub license](https://img.shields.io/github/license/HighCapable/KavaRef?color=blue&style=flat-square)](https://github.com/HighCapable/KavaRef/blob/master/LICENSE) +[![GitHub release](https://img.shields.io/github/v/release/HighCapable/KavaRef?display_name=release&logo=github&color=green&style=flat-square)](https://github.com/HighCapable/KavaRef/releases) +[![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram&style=flat-square)](https://t.me/KavaRef) +[![Telegram](https://img.shields.io/badge/discussion%20dev-Telegram-blue.svg?logo=telegram&style=flat-square)](https://t.me/HighCapable_Dev) +[![QQ](https://img.shields.io/badge/discussion-QQ-blue.svg?logo=tencent-qq&logoColor=red&style=flat-square)](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf) + +LOGO + +一个使用 Kotlin 实现的现代化 Java 反射。 + +[English](README.md) | 简体中文 + +| LOGO | [HighCapable](https://github.com/HighCapable) | +|-------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| + +这个项目属于上述组织,**点击上方链接关注这个组织**,发现更多好项目。 + +## 这是什么 + +这是一个使用 Kotlin 实现的现代化 Java 反射 API,旨在提供更简洁、更易用的 API,同时保留 Java 反射的强大功能。 + +项目图标由 [MaiTungTM](https://github.com/Lagrio) 设计,名称取自 **K**otlinJ**avaRef**lection,意为使用 Kotlin 实现的 Java 反射。 + +它最早诞生于 [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI),后期被解耦合为 [YukiReflection](https://github.com/HighCapable/YukiReflection) +项目。 + +如你所见,现在 `KavaRef` 是借助 `YukiReflection` 的设计思想完全重构的一套全新 API,它们没有从属关系,并将取代 `YukiReflection` 成为一个全新的反射解决方案。 + +## 开始使用 + +[点击这里](https://highcapable.github.io/KavaRef/zh-cn) 前往文档页面查看更多详细教程和内容。 + +## 项目推广 + + +
+

嘿,还请君留步!👋

+

这里有 Android 开发工具、UI 设计、Gradle 插件、Xposed 模块和实用软件等相关项目。

+

如果下方的项目能为你提供帮助,不妨为我点个 star 吧!

+

所有项目免费、开源,遵循对应开源许可协议。

+

→ 查看更多关于我的项目,请点击这里 ←

+
+ +## Star History + +![Star History Chart](https://api.star-history.com/svg?repos=HighCapable/KavaRef&type=Date) + +## 许可证 + +- [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) + +``` +Apache License Version 2.0 + +Copyright (C) 2019 HighCapable + +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 + + https://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. +``` + +版权所有 © 2019 HighCapable \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5cc07eb --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# KavaRef + +[![GitHub license](https://img.shields.io/github/license/HighCapable/KavaRef?color=blue&style=flat-square)](https://github.com/HighCapable/KavaRef/blob/master/LICENSE) +[![GitHub release](https://img.shields.io/github/v/release/HighCapable/KavaRef?display_name=release&logo=github&color=green&style=flat-square)](https://github.com/HighCapable/KavaRef/releases) +[![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram&style=flat-square)](https://t.me/KavaRef) +[![Telegram](https://img.shields.io/badge/discussion%20dev-Telegram-blue.svg?logo=telegram&style=flat-square)](https://t.me/HighCapable_Dev) +[![QQ](https://img.shields.io/badge/discussion-QQ-blue.svg?logo=tencent-qq&logoColor=red&style=flat-square)](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf) + +LOGO + +A modernizing Java Reflection with Kotlin. + +English | [简体中文](README-zh-CN.md) + +| LOGO | [HighCapable](https://github.com/HighCapable) | +|-------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| + +This project belongs to the above-mentioned organization, **click the link above to follow this organization** and discover more good projects. + +## What's this + +This is a modern Java reflection API implemented using Kotlin, designed to provide a cleaner and easier-to-use API while retaining the power of Java +reflection. + +The project icon is designed by [MaiTungTM](https://github.com/Lagrio) and is named from **K**otlinJ**avaRef**lection, meaning Java reflection +implemented using Kotlin. + +It was firstborn in the [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI), and was later decoupled into +the [YukiReflection](https://github.com/HighCapable/YukiReflection) project. + +As you can see, now `KavaRef` is a completely new set of APIs completely refactored with the design idea of `YukiReflection`, +which has no affiliation and will replace `YukiReflection` as a new reflection solution. + +## Get Started + +[Click here](https://highcapable.github.io/KavaRef/en) go to the documentation page for more detailed tutorials and content. + +## Promotion + + +
+

Hey, please stay! 👋

+

Here are related projects such as Android development tools, UI design, Gradle plugins, Xposed Modules and practical software.

+

If the project below can help you, please give me a star!

+

All projects are free, open source, and follow the corresponding open source license agreement.

+

→ To see more about my projects, please click here ←

+
+ +## Star History + +![Star History Chart](https://api.star-history.com/svg?repos=HighCapable/KavaRef&type=Date) + +## License + +- [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) + +``` +Apache License Version 2.0 + +Copyright (C) 2019 HighCapable + +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 + + https://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. +``` + +Copyright © 2019 HighCapable \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..1405c02 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,57 @@ +import org.jetbrains.dokka.gradle.DokkaTask + +plugins { + autowire(libs.plugins.kotlin.jvm) apply false + autowire(libs.plugins.kotlin.dokka) apply false + autowire(libs.plugins.maven.publish) apply false +} + +libraryProjects { + afterEvaluate { + configure { + repositories { + val repositoryDir = gradle.gradleUserHomeDir + .resolve("highcapable-maven-repository") + .resolve("repository") + maven { + name = "HighCapableMavenReleases" + url = repositoryDir.resolve("releases").toURI() + } + maven { + name = "HighCapableMavenSnapShots" + url = repositoryDir.resolve("snapshots").toURI() + } + } + } + } + tasks.withType().configureEach { + val configuration = """{ "footerMessage": "KavaRef | Apache-2.0 License | Copyright (C) 2019 HighCapable" }""" + pluginsMapConfiguration.set(mapOf("org.jetbrains.dokka.base.DokkaBase" to configuration)) + } + tasks.register("publishKDoc") { + group = "documentation" + dependsOn("dokkaHtml") + doLast { + val docsDir = rootProject.projectDir + .resolve("docs-source") + .resolve("dist") + .resolve("KDoc") + .resolve(project.name) + if (docsDir.exists()) docsDir.deleteRecursively() else docsDir.mkdirs() + layout.buildDirectory.dir("dokka/html").get().asFile.copyRecursively(docsDir) + } + } +} + +fun libraryProjects(action: Action) { + val libraries = listOf( + Libraries.KAVAREF_CORE, + Libraries.KAVAREF_EXTENSION + ) + allprojects { if (libraries.contains(name)) action.execute(this) } +} + +object Libraries { + const val KAVAREF_CORE = "kavaref-core" + const val KAVAREF_EXTENSION = "kavaref-extension" +} \ No newline at end of file diff --git a/docs-source/.gitignore b/docs-source/.gitignore new file mode 100644 index 0000000..8de9280 --- /dev/null +++ b/docs-source/.gitignore @@ -0,0 +1,4 @@ +/node_modules +/src/.vuepress/.cache +/src/.vuepress/.temp +/dist \ No newline at end of file diff --git a/docs-source/.vscode/settings.json b/docs-source/.vscode/settings.json new file mode 100644 index 0000000..3b66410 --- /dev/null +++ b/docs-source/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "git.ignoreLimitWarning": true +} \ No newline at end of file diff --git a/docs-source/build-dokka.sh b/docs-source/build-dokka.sh new file mode 100755 index 0000000..c0056a3 --- /dev/null +++ b/docs-source/build-dokka.sh @@ -0,0 +1,2 @@ +cd .. +./gradlew :kavaref-core:publishKDoc :kavaref-extension:publishKDoc \ No newline at end of file diff --git a/docs-source/package.json b/docs-source/package.json new file mode 100644 index 0000000..c5aa318 --- /dev/null +++ b/docs-source/package.json @@ -0,0 +1,17 @@ +{ + "name": "kavaref_docs", + "license": "Apache-2.0", + "devDependencies": { + "@mr-hope/vuepress-plugin-copy-code": "^1.30.0", + "@vuepress/plugin-prismjs": "2.0.0-rc.0", + "@vuepress/plugin-search": "2.0.0-rc.0", + "@vuepress/plugin-shiki": "2.0.0-rc.0", + "vuepress": "2.0.0-rc.0" + }, + "scripts": { + "docs:dev": "vuepress dev src", + "docs:build": "vuepress build src", + "docs:build-gh-pages": "vuepress build src && touch dist/.nojekyll && sh build-dokka.sh" + }, + "dependencies": {} +} \ No newline at end of file diff --git a/docs-source/src/.vuepress/config.ts b/docs-source/src/.vuepress/config.ts new file mode 100644 index 0000000..5ae6bc7 --- /dev/null +++ b/docs-source/src/.vuepress/config.ts @@ -0,0 +1,64 @@ +import { defaultTheme } from 'vuepress'; +import { shikiPlugin } from '@vuepress/plugin-shiki'; +import { searchPlugin } from '@vuepress/plugin-search'; +import { navBarItems, sideBarItems, configs, pageLinkRefs } from './configs/template'; +import { env, markdown } from './configs/utils'; + +export default { + dest: configs.dev.dest, + port: configs.dev.port, + base: configs.website.base, + head: [['link', { rel: 'icon', href: configs.website.icon }]], + title: configs.website.title, + description: configs.website.locales['/en/'].description, + locales: configs.website.locales, + theme: defaultTheme({ + logo: configs.website.logo, + repo: configs.github.repo, + docsRepo: configs.github.repo, + docsBranch: configs.github.branch, + docsDir: configs.github.dir, + editLinkPattern: ':repo/edit/:branch/:path', + sidebar: sideBarItems, + sidebarDepth: 2, + locales: { + '/en/': { + navbar: navBarItems['/en/'], + selectLanguageText: 'English (US)', + selectLanguageName: 'English', + editLinkText: 'Edit this page on GitHub', + tip: 'Tips', + warning: 'Notice', + danger: 'Pay Attention', + }, + '/zh-cn/': { + navbar: navBarItems['/zh-cn/'], + selectLanguageText: '简体中文 (CN)', + selectLanguageName: '简体中文', + editLinkText: '在 GitHub 上编辑此页', + notFound: ['这里什么都没有', '我们怎么到这来了?', '这是一个 404 页面', '看起来我们进入了错误的链接'], + backToHome: '回到首页', + contributorsText: '贡献者', + lastUpdatedText: '上次更新', + tip: '小提示', + warning: '注意', + danger: '特别注意', + openInNewWindow: '在新窗口中打开', + toggleColorMode: '切换颜色模式' + } + }, + }), + extendsMarkdown: (md: markdownit) => { + markdown.injectLinks(md, env.dev ? pageLinkRefs.dev : pageLinkRefs.prod); + }, + plugins: [ + shikiPlugin({ theme: 'github-dark-dimmed' }), + searchPlugin({ + isSearchable: (page) => page.path !== '/', + locales: { + '/en/': { placeholder: 'Search' }, + '/zh-cn/': { placeholder: '搜索' } + } + }) + ] +}; \ No newline at end of file diff --git a/docs-source/src/.vuepress/configs/template.ts b/docs-source/src/.vuepress/configs/template.ts new file mode 100644 index 0000000..6b5aed8 --- /dev/null +++ b/docs-source/src/.vuepress/configs/template.ts @@ -0,0 +1,148 @@ +import { i18n } from './utils'; + +interface PageLinkRefs { + dev: Record[]; + prod: Record[]; +} + +const navigationLinks = { + start: [ + '/guide/home', + '/guide/quick-start' + ], + library: [ + '/library/kavaref-core', + '/library/kavaref-extension' + ], + config: [ + '/config/r8-proguard', + '/config/processor-resolvers', + '/config/migration' + ], + about: [ + '/about/changelog', + '/about/future', + '/about/contacts', + '/about/about' + ] +}; + +export const configs = { + dev: { + dest: 'dist', + port: 9000 + }, + website: { + base: '/KavaRef/', + icon: '/KavaRef/images/logo.svg', + logo: '/images/logo.svg', + title: 'KavaRef', + locales: { + '/en/': { + lang: 'en-US', + description: 'A modernizing Java Reflection with Kotlin' + }, + '/zh-cn/': { + lang: 'zh-CN', + description: '一个使用 Kotlin 实现的现代化 Java 反射' + } + } + }, + github: { + repo: 'https://github.com/HighCapable/KavaRef', + page: 'https://highcapable.github.io/KavaRef', + branch: 'main', + dir: 'docs-source/src' + } +}; + +export const pageLinkRefs: PageLinkRefs = { + dev: [ + { 'repo://': `${configs.github.repo}/` }, + // KDoc URL for local debugging, non-fixed value, adjust according to your own needs. + // You can run ./build-dokka.sh and start the local server in dist/KDoc. + { 'kdoc://': 'http://localhost:9001/' } + ], + prod: [ + { 'repo://': `${configs.github.repo}/` }, + { 'kdoc://': `${configs.github.page}/KDoc/` } + ] +}; + +export const navBarItems = { + '/en/': [{ + text: 'Navigation', + children: [{ + text: 'Get Started', + children: i18n.array(navigationLinks.start, 'en') + }, { + text: 'Libraries', + children: i18n.array(navigationLinks.library, 'en') + }, { + text: 'Configs', + children: i18n.array(navigationLinks.config, 'en') + }, { + text: 'About', + children: i18n.array(navigationLinks.about, 'en') + }] + }, { + text: 'Contact Us', + link: i18n.string(navigationLinks.about[2], 'en') + }], + '/zh-cn/': [{ + text: '导航', + children: [{ + text: '入门', + children: i18n.array(navigationLinks.start, 'zh-cn') + }, { + text: '依赖', + children: i18n.array(navigationLinks.library, 'zh-cn') + }, { + text: '配置', + children: i18n.array(navigationLinks.config, 'zh-cn') + }, { + text: '关于', + children: i18n.array(navigationLinks.about, 'zh-cn') + }] + }, { + text: '联系我们', + link: i18n.string(navigationLinks.about[2], 'zh-cn') + }] +}; + +export const sideBarItems = { + '/en/': [{ + text: 'Get Started', + collapsible: true, + children: i18n.array(navigationLinks.start, 'en') + }, { + text: 'Libraries', + collapsible: true, + children: i18n.array(navigationLinks.library, 'en') + }, { + text: 'Configs', + collapsible: true, + children: i18n.array(navigationLinks.config, 'en') + }, { + text: 'About', + collapsible: true, + children: i18n.array(navigationLinks.about, 'en') + }], + '/zh-cn/': [{ + text: '入门', + collapsible: true, + children: i18n.array(navigationLinks.start, 'zh-cn') + }, { + text: '依赖', + collapsible: true, + children: i18n.array(navigationLinks.library, 'zh-cn') + }, { + text: '配置', + collapsible: true, + children: i18n.array(navigationLinks.config, 'zh-cn') + }, { + text: '关于', + collapsible: true, + children: i18n.array(navigationLinks.about, 'zh-cn') + }] +}; \ No newline at end of file diff --git a/docs-source/src/.vuepress/configs/utils.ts b/docs-source/src/.vuepress/configs/utils.ts new file mode 100644 index 0000000..dbd8f13 --- /dev/null +++ b/docs-source/src/.vuepress/configs/utils.ts @@ -0,0 +1,39 @@ +export const env = { + dev: process.env.NODE_ENV === 'development' +}; + +export const i18n = { + space: ' ', + string: (content: string, locale: string) => { + return '/' + locale + content; + }, + array: (contents: string[], locale: string) => { + const newContents: string[] = []; + contents.forEach((content) => { + newContents.push(i18n.string(content, locale)); + }); + return newContents; + } +}; + +export const markdown = { + injectLinks: (md: markdownit, maps: Record[]) => { + const defaultRender = md.renderer.rules.link_open || function (tokens, idx, options, _env, self) { + return self.renderToken(tokens, idx, options); + }; + md.renderer.rules.link_open = function (tokens, idx, options, env, self) { + const hrefIndex = tokens[idx].attrIndex('href'); + let current = tokens[idx].attrs!![hrefIndex][1]; + for (const map of maps) { + for (const [search, replace] of Object.entries(map)) { + if (current.startsWith(search)) { + current = current.replace(search, replace); + tokens[idx].attrs!![hrefIndex][1] = current; + break; + } + } + } + return defaultRender(tokens, idx, options, env, self); + }; + } +}; \ No newline at end of file diff --git a/docs-source/src/.vuepress/public/images/logo.svg b/docs-source/src/.vuepress/public/images/logo.svg new file mode 100644 index 0000000..fe777cc --- /dev/null +++ b/docs-source/src/.vuepress/public/images/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/docs-source/src/.vuepress/styles/index.scss b/docs-source/src/.vuepress/styles/index.scss new file mode 100644 index 0000000..c1b627a --- /dev/null +++ b/docs-source/src/.vuepress/styles/index.scss @@ -0,0 +1,179 @@ +$primary-color: rgb(59, 85, 113); +$accent-color: rgb(71, 103, 137); +$content-width: 965px; +$scroll-bar-width: 8px; +$scroll-bar-height: 6.5px; +$scroll-bar-border-radius: 50px; +$scroll-bar-track-color-code: rgb(86, 96, 110); +$scroll-bar-thumb-hover-color-code: rgb(121, 135, 155); + +:root { + --c-brand: #{$primary-color}; + --c-brand-light: #{$accent-color}; + --content-width: #{$content-width}; +} + +code { + padding: 3px 5px 3px 5px; + border-radius: 5px; +} + +.badge { + margin-bottom: 5px; +} + +.custom-container { + border-radius: 5px; +} + +.sidebar-item { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.language-text { + ::-webkit-scrollbar-track { + background: #{$scroll-bar-track-color-code}; + border-radius: #{$scroll-bar-border-radius}; + } + + ::-webkit-scrollbar-thumb:hover { + background: #{$scroll-bar-thumb-hover-color-code}; + } +} + +.language-kotlin { + ::-webkit-scrollbar-track { + background: #{$scroll-bar-track-color-code}; + border-radius: #{$scroll-bar-border-radius}; + } + + ::-webkit-scrollbar-thumb:hover { + background: #{$scroll-bar-thumb-hover-color-code}; + } +} + +.language-java { + ::-webkit-scrollbar-track { + background: #{$scroll-bar-track-color-code}; + border-radius: #{$scroll-bar-border-radius}; + } + + ::-webkit-scrollbar-thumb:hover { + background: #{$scroll-bar-thumb-hover-color-code}; + } +} + +.language-groovy { + ::-webkit-scrollbar-track { + background: #{$scroll-bar-track-color-code}; + border-radius: #{$scroll-bar-border-radius}; + } + + ::-webkit-scrollbar-thumb:hover { + background: #{$scroll-bar-thumb-hover-color-code}; + } +} + +.language-xml { + ::-webkit-scrollbar-track { + background: #{$scroll-bar-track-color-code}; + border-radius: #{$scroll-bar-border-radius}; + } + + ::-webkit-scrollbar-thumb:hover { + background: #{$scroll-bar-thumb-hover-color-code}; + } +} + +.hidden-anchor-page { + h6 { + color: transparent; + margin-bottom: -35px; + padding-top: 50px; + } +} + +.code-page { + h1 { + font-size: 24pt; + } + + h2 { + font-size: 18pt; + } + + h3 { + font-size: 15pt; + } + + h4 { + font-size: 12pt; + } + + h5 { + font-size: 9.6pt; + } + + h6 { + font-size: 8.4pt; + } + + .symbol { + color: rgb(142, 155, 168); + } + + .deprecated { + color: rgb(142, 155, 168); + text-decoration: line-through; + } +} + +html { + scroll-behavior: smooth; + + ::-webkit-scrollbar { + width: #{$scroll-bar-width}; + height: #{$scroll-bar-height}; + } + + ::-webkit-scrollbar-track { + background: rgb(234, 236, 239); + } + + ::-webkit-scrollbar-thumb { + background: rgb(189, 189, 189); + border-radius: #{$scroll-bar-border-radius}; + } + + ::-webkit-scrollbar-thumb:hover { + background: rgb(133, 133, 133); + border-radius: #{$scroll-bar-border-radius}; + } +} + +html.dark { + --c-brand: #{$primary-color}; + --c-brand-light: #{$accent-color}; + --content-width: #{$content-width}; + + ::-webkit-scrollbar { + width: #{$scroll-bar-width}; + height: #{$scroll-bar-height}; + } + + ::-webkit-scrollbar-track { + background: rgb(41, 46, 53); + } + + ::-webkit-scrollbar-thumb { + background: rgb(65, 72, 83); + border-radius: #{$scroll-bar-border-radius}; + } + + ::-webkit-scrollbar-thumb:hover { + background: rgb(56, 62, 72); + border-radius: #{$scroll-bar-border-radius}; + } +} \ No newline at end of file diff --git a/docs-source/src/en/about/about.md b/docs-source/src/en/about/about.md new file mode 100644 index 0000000..50a1b0a --- /dev/null +++ b/docs-source/src/en/about/about.md @@ -0,0 +1,27 @@ +# About This Document + +> This document is powered by [VuePress](https://v2.vuepress.vuejs.org/en). + +## License + +[Apache-2.0](https://github.com/HighCapable/KavaRef/blob/main/LICENSE) + +```:no-line-numbers +Apache License Version 2.0 + +Copyright (C) 2019 HighCapable + +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 + + https://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. +``` + +Copyright © 2019 HighCapable \ No newline at end of file diff --git a/docs-source/src/en/about/changelog.md b/docs-source/src/en/about/changelog.md new file mode 100644 index 0000000..a781140 --- /dev/null +++ b/docs-source/src/en/about/changelog.md @@ -0,0 +1,29 @@ +# Changelog + +> The version update history of `KavaRef` is recorded here. + +::: danger + +We will only maintain the latest API version, if you are using an outdate API version, you voluntarily renounce any possibility of maintenance. + +::: + +::: warning + +To avoid translation time consumption, Changelog will use **Google Translation** from **Chinese** to **English**, please refer to the original text for actual reference. + +Time zone of version release date: **UTC+8** + +::: + +## kavaref-core + +### 1.0.0 | 2025.06.25   + +- The first version is submitted to Maven + +## kavaref-extension + +### 1.0.0 | 2025.06.25   + +- The first version is submitted to Maven \ No newline at end of file diff --git a/docs-source/src/en/about/contacts.md b/docs-source/src/en/about/contacts.md new file mode 100644 index 0000000..88b2c8d --- /dev/null +++ b/docs-source/src/en/about/contacts.md @@ -0,0 +1,16 @@ +# Contact Us + +> If you have any questions in use, or have any constructive suggestions, you can contact us. + +Join our developers group. + +- [Click to join Telegram group](https://t.me/KavaRef) +- [Click to join Telegram group (Developer))](https://t.me/HighCapable_Dev) + +Find me on **Twitter** [@fankesyooni](https://twitter.com/fankesyooni). + +## Help with Maintenance + +Thank you for choosing and using `KavaRef`. + +If you have code-related suggestions and requests, you can submit a Pull Request on GitHub. \ No newline at end of file diff --git a/docs-source/src/en/about/future.md b/docs-source/src/en/about/future.md new file mode 100644 index 0000000..c263026 --- /dev/null +++ b/docs-source/src/en/about/future.md @@ -0,0 +1,114 @@ +# Looking for Future + +> The future is bright and uncertain, let us look forward to the future development space of `KavaRef`. + +## Future Plans + +> Features that `KavaRef` may add later are included here. + +### Supports Class Filtering through ClassLoader + +`KavaRef` currently only supports search and calls for reflection APIs such as `Method`, `Field`, and `Constructor`. +In the future, the ability to filter `Class` by specified type `ClassLoader` may be supported in Java and Android platforms according to requirements. + +Currently, you can use [DexKit](https://github.com/LuckyPray/DexKit) to complete this requirement, +which also supports more complex searches and calls of reflective APIs such as Method, Field, and Constructor. + +### Automatically Generate Reflection Code + +**This is a feature that has been initially established in [YukiReflection](https://github.com/HighCapable/YukiReflection), and `KavaRef` is ready to continue to implement it in the possible time in the future.** + +Use `stub` to create a Kotlin class, and declare the parameters in it, as well as its different states in each version. + +For example, the Java class below is the target class we need to reflect. + +> The following example + +```java:no-line-numbers +package com.example.test; + +public class MyClass { + + private String myField = "test"; + + public MyClass() { + //... + } + + private String myMethod1(String var1, int var2) { + //... + } + + private void myMethod2() { + //... + } + + private void myMethod3(String var1) { + //... + } +} +``` + +Through the existing usage of the current API, this class can be called reflectively in the following way. + +> The following example + +```kotlin +MyClass().resolve().apply { + // Call myField. + val value = firstField { name = "myField" }.get() + // Call myMethod1. + val methodValue = firstMethod { name = "myMethod1" }.invoke("test", 0) + // Call myMethod2. + firstMethod { name = "myMethod2" }.invoke() + // Call myMethod3. + firstMethod { name = "myMethod3" }.invoke("test") +} +``` + +The function to be implemented at present can be directly defined as the following Kotlin class using the reflection function. + +> The following example + +```kotlin +package com.example.test + +@ReflectClass +class MyClass { + + @ReflectField + val myField: String = fieldValueOf("none") + + @ReflectMethod + fun myMethod1(var1: String, var2: Int): String = methodReturnValueOf("none") + + @ReflectMethod + fun myMethod2() = MethodReturnType.Unit + + @ReflectMethod + fun myMethod3(var1: String) = MethodReturnType.Unit +} +``` + +Then we can directly call this defined Kotlin class to implement the reflection function, and the API will automatically generate the reflection code according to the annotation. + +> The following example + +```kotlin +MyClass().also { + // Call myField + val value = it.myField + // Call myMethod1 + val methodValue = it.myMethod1("test", 0) + // Call myMethod2 + it.myMethod2() + // Call myMethod3 + it.myMethod3("test") +} +``` + +::: tip + +The above functions may change after the actual release, and the functions of the actual version shall prevail. + +::: \ No newline at end of file diff --git a/docs-source/src/en/config/migration.md b/docs-source/src/en/config/migration.md new file mode 100644 index 0000000..ef6dbd0 --- /dev/null +++ b/docs-source/src/en/config/migration.md @@ -0,0 +1,266 @@ +# Migration to KavaRef + +If you are used to using the reflection API in [YukiReflection](https://github.com/HighCapable/YukiReflection) or [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI), you can refer to the following to migrate to `KavaRef`. + +::: warning + +For `YukiHookAPI`, you need to continue using its Hook API, and `KavaRef` only includes Java reflection-related APIs. + +::: + +## Basic Functions + +The design concept of `KavaRef` is similar to `YukiReflection`, but not exactly the same. +The following lists the differences between `YukiReflection` and `KavaRef` in basic reflection functions, which you can manually migrate based on. + +For example, we have the following Java class. + +> The following example + +```java :no-line-numbers +public class MyClass { + + private void myMethod(String content) { + System.out.println("Hello " + content + "!"); + } +} +``` + +Here is a comparison of `KavaRef` with `YukiReflection` using examples. + +> The following example + +
+ +
+

KavaRef

+ +```kotlin +// Assume that's your MyClass instance. +val myClass: MyClass +// Call and execute using KavaRef. +MyClass::class.resolve().firstMethod { + name = "myMethod" + parameters(String::class) +}.of(myClass).invoke("Hello, KavaRef!") +// Direct reference to instance. +myClass.resolve().firstMethod { + name = "myMethod" + parameters(String::class) +}.invoke("Hello, KavaRef!") +``` + +
+ +
+

YukiReflection

+ +```kotlin +// Assume that's your MyClass instance. +val myClass: MyClass +// Call and execute using YukiReflection. +MyClass::class.java.method { + name = "myMethod" + param(StringClass) +}.get(myClass).call("Hello, YukiReflection!") +// Direct reference to instance. +myClass.current().method { + name = "myMethod" + param(StringClass) +}.call("Hello, YukiReflection!") +``` + +
+
+ +`KavaRef` starts reflection at any time, you need to use `resolve()` to create a reflection scope. +You no longer directly extend the related `method` and `constructor` methods to avoid contaminating their scope. + +`KavaRef` abandons the "Finder" design concept and uses the "Filter" design concept to obtain reflected results. +"Find" is no longer a finding, but a "filtering". + +`KavaRef` canceled the `YukiReflection` defined in the resulting instance whether the `Member` obtained is a multiple or a single design scheme, +and directly returns the entire `List`. +The example you see above uses `firstMethod` to get the first match `MethodResolver`, +if you need to get all matches, you can change to `method`. + +The conditional method name of `KavaRef` in `MethodCondition` has been modified from abbreviation +such as `param` before `YukiReflection` to `parameters` to more in line with the naming habit of Java reflection API. + +`KavaRef` no longer provides the `param(...).order()` function in the condition, because this function itself is unstable. +`KavaRef` now uses an iterator for filtering, and the bytecode will no longer be in order, and the bytecode should not be filtered in order. +You can use `firstMethod`, `firstField`, or `lastMethod`, `lastField`, etc. to get the first or last match result. + +`KavaRef` renames the `get(instance)` method to `of(instance)` because `get(...)` may be confused with the `get(...)` usage of `Field` and is not semantic, +At the same time, `get(instance)` is no longer getting the `MethodFinder.Result.Instance` instance from something like `MethodFinder.Result`, +but uses `of(instance)` to always operate and set the instance object to `MemberResolver`. + +Methods such as `string()`, `int()`, etc. in `MethodFinder.Result.Instance` have been removed in `KavaRef`. +You can directly use `get()`, `get()`, `invoke(...)`, `invoke(...)`, etc. to get or call the corresponding type results. + +::: danger + +If you are looking for (filtering) `Field`, you need to note that there may be semantic conflicts between `KavaRef` and `YukiReflection` in the acquisition method of `Field`. +Please pay special attention when migrating this part. + +For example, get the static field of `content` in `MyClass`, in `YukiReflection`, you would do this. + +> The following example + +```kotlin +MyClass::class.java + .field { name = "content" } // Return FieldFinder.Result + .get() // Cannot be omitted, return FieldFinder.Result.Instance + .string() // value +``` + +In `KavaRef` you need to do this. + +> The following example + +```kotlin +MyClass::class.resolve() + .firstField { name = "content" } // Return FieldResolver + .get() // value +``` + +As mentioned above, `get(...)` is used to get the `FieldFinder.Result.Instance` object in Y`ukiReflection`, not the value. +To get the value and process it as a specified type, you need to call `string()` or `cast()`, and in `KavaRef`, +you use `get()` directly in `MemberResolver` to get the value of the specified type. +The usage of `get(...)` of `KavaRef` for `get(...)` to `of(...)`. + +So the complete writing of the above example in `KavaRef` should be. + +> The following example + +```kotlin +// Since the call is a static instance, "of(null)" can be omitted. +MyClass::class.resolve() + .firstField { name = "content" } // It's already a call chain object FieldResolver + .of(null) // Can be omitted and return to the call chain object FieldResolver + .get() // value +``` + +::: + +`KavaRef` no longer provides `call` methods for `Method`, and is now merged uniformly into `invoke` (with generic parameters). +At the same time, `KavaRef` defines the `newInstance` method of `Constructor` as `create` (with generic parameters). + +You may have noticed that the condition `superClass()` is gone, it is still there, +in `KavaRef` it has been renamed to `superclass()`, docking with the standard Java reflection API. + +At the same time, `KavaRef` extends `KClass`, and you no longer need to use `Some::class.java` to declare an instance of `Class` in most scenarios. + +Another design idea of ​​`KavaRef` is type safety. +As long as you use `KClass` and `Class` that declare the generic type, it will be checked and converted to the +corresponding type when `of(instance)` and `create(...)`, and type checking will be completed during coding to avoid runtime errors. + +> The following example + +```kotlin +// Assume that's your MyClass instance. +val myClass: MyClass +// Using KavaRef to call and execute. +MyClass::class + .resolve() + .firstMethod { + name = "myMethod" + parameters(String::class) + } + // Only instances of type MyClass can be passed in. + .of(myClass) + .invoke("Hello, KavaRef!") +``` + +## Other Functions + +`KavaRef` and `YukiReflection` are not much different in other functions and extended functions. +`KavaRef` separates these functions into a separate module. + +The following functionality is provided in `YukiReflection` but is not implemented and no longer provided in `KavaRef`: + +- Preset reflection type constant classes, such as `StringClass`, `IntType`, etc + - You can use Kotlin class references such as `String::class`, `Int::class`, etc. to replace it. + For primitive types and wrapper classes, `IntType` is equivalent to `Int::class`, and `IntClass` is equivalent to `JInteger::class` + +- `DexClassFinder` function + - Due to its design flaws and possible performance issues when used on Android platforms, it is no longer available for now + +- `RemedyPlan` and `method { ... } .remedys { ... }` functions + - Due to possible black box problems, maintenance is relatively difficult. + If you need to use similar functions, please implement them manually and will no longer be provided + +- `ClassLoader.listOfClasses()` function + - Due to the complex and unstable implementation solutions of each platform, it will no longer be provided + +- `ClassLoader.searchClass()` function + - Due to performance issues and design time is limited to Android platform, + it is relatively difficult to maintain filter conditions and is no longer provided + +- `Class.hasExtends`, `Class.extends`, `Class.implements` functions + - You can replace them with `A::class isSubclassOf B::class` + +- `Class.toJavaPrimitiveType()` function + - There is conceptual confusion in functional design and will no longer be provided + +- `"com.some.clazz".hasClass(loader)` function + - You can use `loader.hasClass("com.some.clazz")` to replace it + +- `Class.hasField`, `Class.hasMethod`, `Class.hasConstructor` functions + - Due to design defects, no longer provided + +- `Class.hasModifiers(...)`, `Member.hasModifiers(...)` functions + - You can replace them directly with extension methods such as `Class.isPublic`, `Member.isPublic` + +- `Class.generic()`, `GenericClass` functions + - If you just want to get generic parameters of the superclass, you can use `Class.genericSuperclassTypeArguments()`. + Due to design defects, no longer provided + +- `Class.current()`, `CurrentClass` functions + - Merged into the core function of `KavaRef.resolve()` and is no longer provided separately + +- `Class.buildOf(...)` function + - You can use `Class.createInstance(...)` to replace it + +- `Class.allMethods()`, `Class.allFields()`, `Class.allConstructors()` functions + - Due to its pollution scope, no longer provided + +- `YLog` log function + - `KavaRef` no longer takes over the logs, you can use the corresponding platform implementation method and no longer provided + +## Exception Handling + +`KavaRef` is completely different from `YukiReflection` in exception handling. +The exception logic of `KavaRef` will remain transparent by default. **It no longer actively intercepts exceptions and prints error logs or even provides `onNoSuchMethod` listener**. +When no valid members are filtered, `KavaRef` will throw an exception directly unless you **explicitly declare the condition as optional (consistent with `YukiReflection` logic)**. + +> The following example + +```kotlin +// Assume that's your MyClass instance. +val myClass: MyClass +// Using KavaRef to call and execute. +MyClass::class + .resolve() + .optional() // Declare as optional, do not throw exceptions. + // Use firstMethodOrNull instead of firstMethod, + // because the NoSuchElementException of Kotlin itself will be thrown. + .firstMethodOrNull { + name = "doNonExistentMethod" // Assume that this method does not exist. + parameters(String::class) + }?.of(myClass)?.invoke("Hello, KavaRef!") +``` + +For more information, please refer to the [Exception Handling](../library/kavaref-core.md#exception-handling) section in [kavaref-core](../library/kavaref-core.md). + +## New to KavaRef + +If you haven't used `YukiReflection` or `YukiHookAPI`, it doesn't matter, you can refer to the following content to get started quickly. + +::: tip What to Do Next + +For more information, please continue reading [kavaref-core](../library/kavaref-core.md) and [kavaref-extension](../library/kavaref-extension.md). + +Get started using `KavaRef` now! + +::: \ No newline at end of file diff --git a/docs-source/src/en/config/processor-resolvers.md b/docs-source/src/en/config/processor-resolvers.md new file mode 100644 index 0000000..1a88d12 --- /dev/null +++ b/docs-source/src/en/config/processor-resolvers.md @@ -0,0 +1,37 @@ +# Third-party Member Resolvers + +> Here are some third-party Member resolvers for reference and use. +> +> Please read [Custom Resolver](../library/kavaref-core.md#custom-resolver) for usage. + +## AndroidHiddenApiBypass + +[Project URL](https://github.com/LSPosed/AndroidHiddenApiBypass) + +> LSPass: Bypass restrictions on non-SDK interfaces + +```kotlin +class AndroidHiddenApiBypassResolver : MemberProcessor.Resolver() { + + override fun getDeclaredConstructors(declaringClass: Class): List> { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + return super.getDeclaredConstructors(declaringClass) + } + + val constructors = HiddenApiBypass.getDeclaredMethods(declaringClass) + .filterIsInstance>() + .toList() + return constructors + } + + override fun getDeclaredMethods(declaringClass: Class): List { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + return super.getDeclaredMethods(declaringClass) + } + + val methods = HiddenApiBypass.getDeclaredMethods(declaringClass) + .filterIsInstance() + .toList() + return methods + } +} \ No newline at end of file diff --git a/docs-source/src/en/config/r8-proguard.md b/docs-source/src/en/config/r8-proguard.md new file mode 100644 index 0000000..9a49801 --- /dev/null +++ b/docs-source/src/en/config/r8-proguard.md @@ -0,0 +1,6 @@ +# R8 & Proguard Obfuscate + +> In most scenarios, Android application installation packages can compress volumes through obfuscation. +> Here is a configuration method for obfuscation rules. + +`KavaRef` does not require any additional configuration of obfuscation rules in Android projects. \ No newline at end of file diff --git a/docs-source/src/en/guide/home.md b/docs-source/src/en/guide/home.md new file mode 100644 index 0000000..9f1cd3c --- /dev/null +++ b/docs-source/src/en/guide/home.md @@ -0,0 +1,46 @@ +# Introduce + +> `KavaRef` is a modern Java reflection API implemented using Kotlin. + +## Background + +This is a modern Java reflection API implemented using Kotlin, designed to provide a cleaner and easier-to-use API while retaining the power of Java +reflection. + +The project icon is designed by [MaiTungTM](https://github.com/Lagrio) and is named from **K**otlinJ**avaRef**lection, meaning Java reflection +implemented using Kotlin. + +It was firstborn in the [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI), and was later decoupled into +the [YukiReflection](https://github.com/HighCapable/YukiReflection) project. + +As you can see, now `KavaRef` is a completely new set of APIs completely refactored with the design idea of ​`YukiReflection`, +which has no affiliation and will replace `YukiReflection` as a new reflection solution. + +If you are using `YukiReflection` or the `YukiHookAPI` project related to it, you can refer to [here](../config/migration) to migrate the reflection API to `KavaRef`. + +## Usage + +`KavaRef` is built in Kotlin **lambda** syntax with Java Builder style. + +It can replace [Java's native Reflection API](https://www.oracle.com/technical-resources/articles/java/javareflection.html) and implement a more complete reflection solution in a more human-friendly language. + +## Skill Requirements + +You must be proficient in Java's native reflection APIs, understand Java's class loading mechanisms, bytecode structures, and how they are used in Kotlin (if you are using Kotlin). + +## Language Requirement + +It is recommended to use Kotlin. API code composition also supports Java, +but in pure Java projects `KavaRef` may not be able to play its full functionality and syntactic sugar advantages. + +All the demo sample code in the documentation will be described first using Kotlin. +If you don't know how to use Kotlin at all, you may not be able to experience and use the functionality of `KavaRef` more fully. + +## Contribution + +The maintenance of this project is inseparable from the support and contributions of all developers. + +This project is currently in its early stages, and there may still be some problems or lack of functions you need. + +If possible, feel free to submit a PR to contribute features you think are needed to this project or goto [GitHub Issues](repo://issues) +to make suggestions to us. \ No newline at end of file diff --git a/docs-source/src/en/guide/quick-start.md b/docs-source/src/en/guide/quick-start.md new file mode 100644 index 0000000..052976d --- /dev/null +++ b/docs-source/src/en/guide/quick-start.md @@ -0,0 +1,97 @@ +# Quick Start + +> Integrate `KavaRef` into your project. + +## Project Requirements + +The project needs to be created using `IntelliJ IDEA` or `Android Studio` and be of type Java or Android +project and have integrated Kotlin environment dependencies. + +- IntelliJ IDEA (It is recommended to get the latest version [from here](https://www.jetbrains.com/idea)) + +- Android Studio (It is recommended to get the latest version [from here](https://developer.android.com/studio)) + +- Kotlin 1.9.0+, Gradle 8+, Java 17+ + +### Configure Repositories + +The dependencies of `KavaRef` are published in **Maven Central** and our public repository, +you can use the following method to configure repositories. + +We recommend using Kotlin DSL as the Gradle build script language and [SweetDependency](https://github.com/HighCapable/SweetDependency) +to manage dependencies. + +#### SweetDependency (Recommended) + +Configure repositories in your project's `SweetDependency` configuration file. + +```yaml +repositories: + google: + maven-central: + # (Optional) You can add this URL to use our public repository + # When Sonatype-OSS fails and cannot publish dependencies, this repository is added as a backup + # For details, please visit: https://github.com/HighCapable/maven-repository + highcapable-maven-releases: + url: https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases +``` + +#### Traditional Method + +Configure repositories in your project `build.gradle.kts`. + +```kotlin +repositories { + google() + mavenCentral() + // (Optional) You can add this URL to use our public repository + // When Sonatype-OSS fails and cannot publish dependencies, this repository is added as a backup + // For details, please visit: https://github.com/HighCapable/maven-repository + maven("https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases") +} +``` + +### Configure Java Version + +Modify the Java version of Kotlin in your project `build.gradle.kts` to 17 or above. + +> Java Project + +```kt +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +kotlin { + jvmToolchain(17) +} +``` + +> Android Project + +```kt +android { + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} +``` + +## Functional Overview + +The project is divided into multiple modules. You can choose the module you wish to include as a dependency in your project, but be sure to include the **kavaref-core** module. + +Click the corresponding module below to view detailed feature descriptions. + +- [kavaref-core](../library/kavaref-core.md) +- [kavaref-extension](../library/kavaref-extension.md) + +## Demo + +You can find some samples [here](repo://tree/main/samples) view the corresponding demo project to better understand how these functions work and quickly +select the functions you need. \ No newline at end of file diff --git a/docs-source/src/en/index.md b/docs-source/src/en/index.md new file mode 100644 index 0000000..a9e0ad4 --- /dev/null +++ b/docs-source/src/en/index.md @@ -0,0 +1,38 @@ +--- +home: true +title: Home +heroImage: /images/logo.svg +actions: + - text: Get Started + link: /en/guide/home + type: primary + - text: Changelog + link: /en/about/changelog + type: secondary +features: + - title: Light and Elegant + details: A powerful, elegant, beautiful API built with Kotlin lambda can help you quickly implement bytecode filtering and reflection functions. + - title: Fully Compatible + details: Using native Java APIs to implement reflection functionality, it can be used on any Kotlin on JVM project, and it is no problem on Android. + - title: Quickly Started + details: Simple and easy to use it now! Do not need complex configuration and full development experience, Integrate dependencies and enjoy yourself. +footer: Apache-2.0 License | Copyright (C) 2019 HighCapable +--- + +### Start reflecting anytime, anywhere. + +```java +public class World { + + private void sayHello(String content) { + System.out.println("Hello " + content + "!"); + } +} +``` + +```kotlin +World().resolve().firstMethod { + name = "sayHello" + parameters(String::class) +}.invoke("KavaRef") +``` \ No newline at end of file diff --git a/docs-source/src/en/library/kavaref-core.md b/docs-source/src/en/library/kavaref-core.md new file mode 100644 index 0000000..af7d2a1 --- /dev/null +++ b/docs-source/src/en/library/kavaref-core.md @@ -0,0 +1,790 @@ +# kavaref-core + +![Maven Central](https://img.shields.io/maven-central/v/com.highcapable.kavaref/kavaref-core?logo=apachemaven&logoColor=orange&style=flat-square) + +![Maven metadata URL](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fraw.githubusercontent.com%2FHighCapable%2Fmaven-repository%2Frefs%2Fheads%2Fmain%2Frepository%2Freleases%2Fcom%2Fhighcapable%2Fkavaref%2Fkavaref-core%2Fmaven-metadata.xml&logo=apachemaven&logoColor=orange&label=highcapable-maven-releases&style=flat-square) + +This is the core dependency of KavaRef, and you need to introduce this module to use the basic features of KavaRef. + +## Configure Dependency + +You can add this module to your project using the following method. + +### SweetDependency (Recommended) + +Add dependency in your project's `SweetDependency` configuration file. + +```yaml +libraries: + com.highcapable.kavaref: + kavaref-core: + version: + +``` + +Configure dependency in your project `build.gradle.kts`. + +```kotlin +implementation(com.highcapable.kavaref.kavaref.core) +``` + +### Version Catalog + +Add dependency in your project's `gradle/libs.versions.toml`. + +```toml +[versions] +kavaref-core = "" + +[libraries] +kavaref-core = { module = "com.highcapable.kavaref:kavaref-core", version.ref = "kavaref-core" } +``` + +Configure dependency in your project `build.gradle.kts`. + +```kotlin +implementation(libs.kavaref.core) +``` + +Please change `` to the version displayed at the top of this document. + +### Traditional Method + +Configure dependency in your project `build.gradle.kts`. + +```kotlin +implementation("com.highcapable.kavaref:kavaref-core:") +``` + +Please change `` to the version displayed at the top of this document. + +## Function Introduction + +You can view the KDoc [click here](kdoc://kavaref-core). + +### Basic Usage + +KavaRef adopts a chained call design, which creates extension methods for available Java reflection APIs (such as `Class`). +You only need to call `resolve()` to these contents to enter the world of KavaRef. + +The relationship diagram is as follows. + +``` :no-line-numbers +KavaRef +└── KClass/Class/Any.resolve() + ├── method() + ├── constructor() + └── field() +``` + +Next, we will give multiple examples of Java `Class`, which will be explained based on them in the future. + +```java :no-line-numbers +package com.demo; + +public class BaseTest { + + public BaseTest() { + // ... + } + + private void doBaseTask(String taskName) { + // ... + } +} +``` + +```java :no-line-numbers +package com.demo; + +public class Test extends BaseTest { + + private Test() { + // ... + } + + private static TAG = "Test"; + + private boolean isTaskRunning = false; + + private void doTask(String taskName) { + // ... + } + + private void release(String taskName, Function task, boolean isFinish) { + // ... + } + + private void stop() { + // ... + } + + private String getName() { + // ... + } +} +``` + +```java +public class Box { + + public void print(T item, String str) { + // ... + } +} +``` + +Suppose, we want to get the `doTask` method of `Test` and execute it. In KavaRef, you can do it in the following ways. + +> The following example + +```kotlin +// Suppose this is an instance of this Class. +val test: Test +//Reflect it by instantiating Class. +// In KavaRef you don't need to convert it to `java.lang.Class`, +// It will automatically call KClass.java. +Test::class + // Create KavaRef reflections. + .resolve() + // Create method condition. + .method { + // Set method name. + name = "doTask" + // Set method parameter type. + parameters(String::class) + } + // After the condition is executed, the matching List + // instance will be returned. + // Here we get the first filtering result. + .first() + // Setting an instance of Test on MethodResolver. + .of(test) + // Calling methods and passing in parameters. + .invoke("task_name") +``` + +In the above writing method, we use `Test::class.resolve()` to get the KavaRef reflection instance of the current `Class`. +Then create a method filtering condition `MethodCondition` by `method { ... }`, which sets the method name and parameter type, and returns the `List` instance after execution. +Then we use `first()` to get the first matched `MethodResolver` instance, +Then use `of(test)` to set the current instance of `Class`, and finally use `invoke("task_name")` to execute the method and pass in the parameters. + +In this, MethodCondition is inherited from MemberCondition, which allows you to conditionally filter the Method, which contains a conditional image of the Java-core reflection API. +You can view the corresponding comments to understand the native usage of each API. + +Similarly, MethodResolver is inherited from `MemberResolver`, which allows you to make reflection calls to `Method` in the filtered result. + +Since the reflection requirement here is to obtain a available method result, the call chain of `method { ... }.first()` may be more cumbersome, +and at this time there is the following simplification solution. + +> The following example + +```kotlin +Test::class + .resolve() + // Use firstMethod directly to get the first matching + // MethodResolver instance. + .firstMethod { + name = "doTask" + parameters(String::class) + } + .of(test) + .invoke("task_name") +``` + +Since we can now get an instance of `Test`, there is also a simplified way to write it, +you can use this instance to create KavaRef reflections directly. + +> The following example + +```kotlin +// Here, the test instance test will be passed to +// KavaRef and get test::class.java. +test.resolve() + .firstMethod { + name = "doTask" + parameters(String::class) + } // Since you set up an instance, of(test) is no longer needed here. + .invoke("task_name") +``` + +Next, we need to get the `isTaskRunning` variable, which can be written in the following form. + +> The following example + +```kotlin +// Suppose this is an example of this Class. +val test: Test +// Call and execute with KavaRef. +val isTaskRunning = test.resolve() + .firstField { + name = "isTaskRunning" + type = Boolean::class + }.get() +``` + +The constructor in `Test` is privatized, and now we can create an instance of it using the following method. + +> The following example + +```kotlin +val test = Test::class.resolve() + .firstConstructor { + // For the zero parameter construction method, + // the following conditions can be used to filter. + // It is equivalent to parameterCount = 0. + emptyParameters() + }.create() // Create a new Test instance. +``` + +You can also use `createAsType()` to specify its superclass type `BaseTest` for the actual object `Test`. + +> The following example + +```kotlin +val test = Test::class.resolve() + .firstConstructor { + emptyParameters() + }.createAsType() // Create a new BaseTest instance. +``` + +::: tip + +In addition to methods such as `firstMethod`, you can also use methods such as `lastMethod` to get the last matching `MethodResolver` instance, which is equivalent to `method { ... }.last()`. + +After you get the `MemberResolver` instance, you can use `self` to get the `Member` original instance of the current `MemberResolver` to do some of your own operations. + +In `MemberResolver` inherited from `InstanceAwareResolver` (for example `MethodResolver` and `FieldResolver`), you can use `of(instance)` +To set the current instance, if the reflection is static (static) member, you do not need to set the instance. + +::: + +::: danger + +In `MemberResolver` inherited from `InstanceAwareResolver`, the type of `of(instance)` requires the same type as the currently reflected `Class` instance generic type. +Unless the `Class` generic type is not specified, or the `Class` generic type is set to `Any`. + +If `of(instance)` appears `Required: Nothing?` error (this is usually due to `Class` created via `Class.forName(...)` or `ClassLoader.loadClass(...)`), +then your `Class` is `Class<*>` (in Java it is `Class`). +At this time, if you do not want to specify the type, please set or convert it to `Class`, just like the following. + +> The following example + +```kotlin +val myClass = Class.forName("com.xxx.MyClass") as Class +// Suppose this is an example of this Class. +val myClassInstance: Any +myClass.resolve() + .firstMethod { + // ... + }.of(myClassInstance).invoke(...) +``` + +You can also use [Create Class Object](kavaref-extension.md#create-class-object) provided in [kavaref-extension](kavaref-extension.md) to solve this problem. + +::: + +### Vague Conditions + +You will notice that there is a `release` method in `Test`, but its method parameters are very long and some types may not be directly obtained. + +At this point, you can use the `parameters(...)` condition to use `VagueType` to fill in the method parameter types you don't want to fill in. + +> The following example + +```kotlin +// Suppose this is an example of this Class. +val test: Test +// Call and execute with KavaRef. +test.resolve() + .firstMethod { + name = "release" + // Use VagueType to fill in the types you don't want to fill in, + // and ensure that other types can match. + parameters(String::class, VagueType, Boolean::class) + } // Get this method. +``` + +::: warning + +`VagueType` can only be used when there are filter conditions with multiple parameters, +it cannot be used in filter conditions with only a single parameter, such as `type`. + +You can create it using `VagueType`, `VagueType::class` or `VagueType::class.java`, all of which are correctly recognized as fuzzy filtering conditions. + +::: + +### Freedom Conditions + +In `MemberCondition`, `name`, `type`, `parameterCount` and other conditions can all use the Kotlin lambda feature to create free filtering conditions. + +Suppose we want to get the `doTask` method in `Test`, we can use the following implementation. + +> The following example + +```kotlin +// Suppose this is an example of this Class. +val test: Test +// Call and execute with KavaRef. +test.resolve() + .firstMethod { + // Use lambda to set the method name. + name { + // Set the name not case sensitive. + it.equals("dotask", ignoreCase = true) + } + // Set parameter type. + parameters(String::class) + }.invoke("task_name") +``` + +### Generic Conditions + +KavaRef supports adding generic filtering conditions, which you can use the relevant functions provided by `TypeMatcher`. + +Suppose we need to filter the `print` method in `Box`. + +> The following example + +```kotlin +// Suppose this is an example of this Class. +val box: Box +// Call and execute with KavaRef. +box.resolve() + .firstMethod { + name = "print" + // Set generic parameter conditions. + genericParametes( + // Filter generic name "T". + typeVar("T"), + // Create TypeMatcher through Class. + String::class.toTypeMatcher() + ) + }.invoke("item", "str") +``` + +### Filter in Superclass + +You will notice that `Test` inherits from `BaseTest`, and now we want to get the `doBaseTask` method of `BaseTest`. + +Without knowing the superclass name, we only need to add `superclass()` to the filter condition to achieve this function. + +> The following example + +```kotlin +// Suppose this is an example of this Class. +val test: Test +// Call and execute with KavaRef. +test.resolve() + .firstMethod { + name = "doBaseTask" + parameters(String::class) + // Just add this condition. + superclass() + }.invoke("task_name") +``` + +At this time, we can get this method in the superclass. + +::: tip + +`superclass()` once set it,it will automatically loop backwards whether there is this method in all inherited +superclasses until the target has no superclass (the inheritance relationship is `java.lang.Object`). + +::: + +::: danger + +The current filtering method can only filter to the current `Class` method unless the `superclass()` condition is specified, +which is the default behavior of the Java reflection API. +KavaRef will call `Class.getDeclaredMethods()` to get the current `Class` method instead of `Class.getMethods()`. + +::: + +### Other Conditions + +KavaRef provides some filtering conditions to assist in the use of the Java reflection API. + +Suppose we want to get the contents of the static variable `TAG` in `Test`. + +In order to reflect that the filtering conditions include static descriptors, we can implement them using the following methods. + +> The following example + +```kotlin +val tag = Test::class.resolve() + .firstField { + name = "TAG" + type = String::class + // Create descriptor filtering. + modifiers(Modifiers.STATIC) + // Or. + modifiers { + it.contains(Modifiers.STATIC) + } + }.get() // Get field content. +``` + +You can also use string types to pass in full class names in conditions such as `type`, `parameters`, etc. + +> The following example + +```kotlin +// Suppose this is an example of this Class. +val test: Test +// Call and execute with KavaRef. +test.resolve() + .firstMethod { + name = "doTask" + // Pass the full class name using string type. + parameters("java.lang.String") + }.invoke("task_name") +``` + +### Exception Handling + +By default, KavaRef throws an exception when a member is not found during a reflection call. + +> The following example + +```kotlin +Test::class.resolve() + .method { + name = "doNonExistentMethod" + } // NoSuchMethodException will be thrown here. +``` + +If you do not want an exception to be thrown, you can set the optional condition `optional()`. + +> The following example + +```kotlin +Test::class.resolve() + // Set optional conditions. + .optional() + .method { + name = "doNonExistentMethod" + + } // Return empty List. +``` + +KavaRef prints the complete exception content for debugging, and when using `optional()`, the exception is printed as a WARN level log. + +> The following example + +``` :no-line-numbers +No method found matching the condition for current class. ++------------------------------------------------+ +| class com.demo | ++------------+-----------------------------------+ +| name | doNonExistentMethod | +| parameters | [class java.lang.String, boolean] | ++------------+-----------------------------------+ +``` + +If you don't want KavaRef to throw or print anything, you can use `optional(silent = true)` to silently handle it, +but we **do not recommend this**, which will mask the problem unless it is necessary. + +::: danger + +If you set `optional()`, please do not use `firstMethod`, `firstConstructor` and other methods to get a single result. +Because they throw an exception with empty list when there is no result, you can use the method with the suffix `OrNull` to get a single result. + +::: + +### Log Management + +KavaRef provides its own log management function, you can set the log level through `KavaRef.logLevel`. + +You can set `KavaRef.logLevel = KavaRefRuntime.LogLevel.DEBUG` to enable DEBUG level logs so that KavaRef +prints more detailed step-by-step filtering condition logs to the console during the filtering process. + +If you want to turn off all log printing of KavaRef, you can set `KavaRef.logLevel = KavaRefRuntime.LogLevel.OFF`. + +If you have more advanced requirements, you can implement `KavaRefRuntime.Logger` to customize your own log printing method. + +> The following example + +```kotlin +class MyLogger : KavaRefRuntime.Logger { + + // Here you can specify the tag for log printing. + override val tag = "MyLogger" + + override fun debug(msg: Any?, throwable: Throwable?) { + // Implement your log printing logic here. + } + + override fun info(msg: Any?, throwable: Throwable?) { + // Implement your log printing logic here. + } + + override fun warn(msg: Any?, throwable: Throwable?) { + // Implement your log printing logic here. + } + + override fun error(msg: Any?, throwable: Throwable?) { + // Implement your log printing logic here. + } +} +``` + +Then, set it to KavaRef. + +> The following example + +```kotlin +KavaRef.setLogger(MyLogger()) +``` + +### Advanced Usage + +The above content explains all the usage methods in standard scenarios. +If you have a more fine-grained usage scenario, you can manually create related components of KavaRef. + +If you don't like the Kotlin lambda writing, you can create chained calls manually. + +> The following example + +```kotlin +// Suppose this is an example of this Class. +val test: Test +// Call and execute with KavaRef. +test.resolve() + .method() // Conditions begin. + .name("doTask") + .parameters(String::class) + .build() // Conditions ends (executes) + .first() + .invoke("task_name") +``` + +You can also manually create any filtering conditions to achieve multiplexing it in any reflection. + +> The following example + +```kotlin +// Suppose this is an example of this Class. +val test: Test +// Create MethodCondition manually. +val condition = MethodCondition() +condition.name = "doTask" +condition.parameters(String::class) +// Apply condition to reflection object. +Test::class.resolve() + .firstMethod(condition) + .of(test) // Setting up instance. + .invoke("task_name") +``` + +Alternatively, you can also manually and completely implement the entire reflection process. + +> The following example + +```kotlin +// Suppose this is an example of this Class. +val test: Test +// Create MethodCondition manually. +val condition = MethodCondition() +condition.name = "doTask" +condition.parameters(String::class) +// Create MemberCondition.Configuration manually. +val configuration = Test::class.java.createConfiguration( + memberInstance = test, // Setting up instance. + processorResolver = null, // Use the default resolver, refer to the "Custom Resolver" below. + superclass = false, // Whether to filter in superclass. + optional = MemberCondition.Configuration.Optional.NO // Configure optional conditions. +) +// Create and start filtering. +val resolvers = condition.build(configuration) +// Get the first result. +val resolver = resolvers.first() +// Execute the method. +resolver.invoke("task_name") +``` + +If you have more advanced requirements for business layer logic, you can also use `mergeWith` to merge multiple filter conditions. + +> The following example + +```kotlin +// Suppose this is an instance of this Class. +val test: Test +// Create MethodCondition manually. +// Create the first condition. +val condition1 = MethodCondition() +condition1.name = "doTask" +// Create a second condition. +val condition2 = MethodCondition() +condition2.parameters(String::class) +// Merge condition2 into condition1. +// At this time, the condition of condition1 will contain the condition that condition2 is not null. +// The duplicated conditions in condition1 will be overwritten by the condition2 condition. +condition1.mergeWith(condition2) +// You can also use the infix syntax. +condition1 mergeWith condition2 +// Call and execute with KavaRef. +Test::class.resolve() + .firstMethod(condition1) + .of(test) + .invoke("task_name") +``` + +::: danger + +Reused use of `build(...)` will no longer be allowed for creation when `MemberCondition.Configuration` is set. +At this point you need to use `copy()` to copy and create a new `MemberCondition`. + +Similarly, `InstanceAwareResolver` is not allowed to duplicately set up new instances after setting the instance via `MemberCondition.Configuration.memberInstance` or `of(instance)`. +At this time, you also need to use `copy()` to copy and create a new `InstanceAwareResolver`. + +::: + +### Custom Resolver + +KavaRef uses the default `Member` resolver for filtering. +If you want to implement your own resolver, you can customize the global and the resolver used for each reflection process. + +You can inherit from `MemberProccessor.Resolver` to implement your own resolver. + +> The following example + +```kotlin +class MyMemberProcessorResolver : MemberProcessor.Resolver() { + + override fun getDeclaredConstructors(declaringClass: Class): List> { + // Intercept and implement your constructor filtering logic here. + return super.getDeclaredConstructors(declaringClass) + } + + override fun getDeclaredMethods(declaringClass: Class): List { + // Intercept and implement your method filtering logic here. + return super.getDeclaredMethods(declaringClass) + } + + override fun getDeclaredFields(declaringClass: Class): List { + // Intercept and implement your field filtering logic here. + return super.getDeclaredFields(declaringClass) + } +} +``` + +You can then set it to the global configuration. + +> The following example + +```kotlin +MemberProcessor.globalResolver = MyMemberProcessorResolver() +``` + +Alternatively, during each reflection, you can set up a custom resolver using `MemberCondition.Configuration` or use a chain call to set up a resolver. + +> The following example + +```kotlin +// Create resolver. +val myResolver = MyMemberProcessorResolver() +// Suppose this is an instance of this Class. +val test: Test +// Call and execute using KavaRef. +test.resolve() + // Set custom resolver. + .processor(myResolver) + .firstMethod { + name = "doTask" + parameters(String::class) + }.invoke("task_name") +``` + +::: tip + +You can find some publicly maintained custom solvers in [here](../config/processor-resolvers.md) and define them in your project to use. + +::: + +### About Cache + +Due to the diversity of filtering conditions, KavaRef does not directly provide caching function, +and the implementation method of caching will also vary depending on the implementation method of each developer. + +We recommend manually implementing caches of `MemberResolver` created with filter results for improved performance +and refer to [Create Manually](#create-manually) to split filter conditions to optimize code reuse. + +::: danger + +If you use `val myResolver by lazy { ... }` to implement the cache, for example, do so below. + +> The following example + +```kotlin +val myResolver by lazy { + Test::class.resolve() + .firstMethod { + name = "doTask" + parameters(String::class) + } +} +``` + +You may do this when calling. + +> The following example + +```kotlin +// Suppose this is an instance of this Class. +val test: Test +// Call and execute using KavaRef. +myResolver.of(test).invoke("task_name") +``` + +Please note that since `MemberResolver` is cached, the same instance is called every time you reference it, +and the instance object of `MemberResolver` is not allowed to be set duplicately (see the "Pay Attention" below [Create Manually](#create-manually)). + +So calling this directly will throw an exception, you need to change it to the following form. + +> The following example + +```kotlin +// Suppose this is an instance of this Class. +val test: Test +// Call and execute using KavaRef. +myResolver.copy().of(test).invoke("task_name") +``` + +This allows you to copy a new `MemberResolver` instance every time you call without repeating the reflection process and throwing an exception. + +::: + +### Java Usage + +KavaRef is not recommended to be used directly in Java because its API design is based on Kotlin's features and syntax sugar. + +If you need to use KavaRef in Java, you can do it in the following ways. + +> The following example + +```java +public class Main { + + public static void main(String[] args) { + // Suppose this is an example of this Class. + Test test; + // Call and execute with KavaRef. + KavaRef.resolveClass(Test.class) + .method() + .name("doTask") + .parameters(String.class) + .build() + .get(0) + .of(test) + .invoke("task_name"); + // Or create KavaRef reflections using an instance. + KavaRef.resolveObject(test) + .method() + .name("doTask") + .parameters(String.class) + .build() + .get(0) + .invoke("task_name"); + } +} +``` \ No newline at end of file diff --git a/docs-source/src/en/library/kavaref-extension.md b/docs-source/src/en/library/kavaref-extension.md new file mode 100644 index 0000000..7009d04 --- /dev/null +++ b/docs-source/src/en/library/kavaref-extension.md @@ -0,0 +1,315 @@ +# kavaref-extension + +![Maven Central](https://img.shields.io/maven-central/v/com.highcapable.kavaref/kavaref-extension?logo=apachemaven&logoColor=orange&style=flat-square) + +![Maven metadata URL](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fraw.githubusercontent.com%2FHighCapable%2Fmaven-repository%2Frefs%2Fheads%2Fmain%2Frepository%2Freleases%2Fcom%2Fhighcapable%2Fkavaref%2Fkavaref-extension%2Fmaven-metadata.xml&logo=apachemaven&logoColor=orange&label=highcapable-maven-releases&style=flat-square) + +This is an extended dependency for KavaRef-related features. + +## Configure Dependency + +You can add this module to your project using the following method. + +### SweetDependency (Recommended) + +Add dependency in your project's `SweetDependency` configuration file. + +```yaml +libraries: + com.highcapable.kavaref: + kavaref-extension: + version: + +``` + +Configure dependency in your project `build.gradle.kts`. + +```kotlin +implementation(com.highcapable.kavaref.kavaref.extension) +``` + +### Version Catalog + +Add dependency in your project's `gradle/libs.versions.toml`. + +```toml +[versions] +kavaref-extension = "" + +[libraries] +kavaref-extension = { module = "com.highcapable.kavaref:kavaref-extension", version.ref = "kavaref-extension" } +``` + +Configure dependency in your project `build.gradle.kts`. + +```kotlin +implementation(libs.kavaref.extension) +``` + +Please change `` to the version displayed at the top of this document. + +### Traditional Method + +Configure dependency in your project `build.gradle.kts`. + +```kotlin +implementation("com.highcapable.kavaref:kavaref-extension:") +``` + +Please change `` to the version displayed at the top of this document. + +## Function Introduction + +You can view the KDoc [click here](kdoc://kavaref-extension). + +### Class Extensions + +KavaRef provides some extensions that are more convenient when dealing with `Class` objects. + +KavaRef also adds the `KClass` extensions to the `Class` extensions, +which is used to call `KClass.java`, which is more concise than using `Some::class.java` directly. + +#### Create Class Object + +For example, we need to create a `Class` object using the string class name. + +> The following example + +```kotlin +val myClass = "com.example.MyClass".toClass() +// You can use a method with OrNull suffix to return null +// when Class is not found instead of throwing an exception. +val myClassOrNull = "com.example.MyClass".toClassOrNull() +``` + +These methods use `ClassLoaderProvider` to get the default `ClassLoader`, +you can set the default `ClassLoader` to affect global functionality. + +> The following example + +```kotlin +ClassLoaderProvider.classLoader = MyCustomClassLoader() +``` + +You can also manually pass a `ClassLoader` parameter to the `toClass` method to specify which `ClassLoader` to use. + +#### Class Object Reference + +Referring to Java Class in Kotlin requires writing a very long statement, +such as `MyClass::class.java`, which you can simplify in the following ways. + +> The following example + +```kotlin +val myClass = classOf() +``` + +You can use the `isSubclassOf` method to determine whether a `Class` is another `Class` subclass. + +> The following example + +```kotlin +val isSubclass = MyClass::class isSubclassOf MySuperClass::class +// Of course, it also has a corresponding antonym of judgment. +val isNotSubclass = MyClass::class isNotSubclassOf MySuperClass::class +``` + +You can also use the `hasSuperclass` and `hasInterfaces` methods to determine whether a `Class` has a superclass or an interface. + +::: danger + +The `Class` passed in by the `classOf` method will perform unboxing of Java wrapper classes by default, +whether you pass in something like `kotlin.Boolean` or `java.lang.Boolean` (see [Java Wrapper Classes Extensions](#java-wrapper-classes-extensions) below), +If you need to avoid the incoming `Class` being unboxed into primitive types, you need to explicitly set the `primitiveType = false` parameter. + +::: + +#### Create New Instance + +KavaRef provides a way for `Class` to easily create a new instance. +You don't need to consider the type of constructing parameters, +you just need to pass in the corresponding parameters to create a new instance immediately. + +> The following example + +```kotlin +val myClass = MyClass::class.createInstance("Hello", 123) +// You can also use a method with the OrNull suffix to return null +// when creation fails instead of throwing an exception. +val myClassOrNull = MyClass::class.createInstanceOrNull("Hello", 123) +// The createInstance method only filters public constructors by default. +// If you need to call non-public constructors, please set isPublic = false. +val myClassWithPrivateConstructor = MyClass::class.createInstance("Private!", isPublic = false) +// If you want to specify the type to create an instance to use another type, +// you can use the following method. +val mySuperClass = MyClass::class.createInstanceAsType("Hello", 123) +// Similarly, you can use a method with the OrNull suffix to return null when +// creation fails instead of throwing an exception. +val mySuperClassOrNull = MyClass::class.createInstanceAsTypeOrNull("Hello", 123) +``` + +::: tip + +After the `createInstance` method is successfully matched once, it will cache the results to prevent performance losses +caused by duplicated reflections. It is thread-safe and you can use it in any standard scenario with confidence. + +::: + +::: danger + +When you pass in a parameter with `null`, KavaRef tries to use it as part of the matchable condition (vague condition), and the accuracy may decrease. + +The `createInstance` method does not allow all parameters to be `null` (the conditions are completely vague), +and an exception will be thrown directly because this situation cannot be determined which instance to create. + +::: + +#### Class Modifier + +KavaRef also extends `Modifier`, you can directly use `Class.isPublic` and other methods to judge a `Class` modifier. + +#### VariousClass + +KavaRef provides the `VariousClass` class to load the `Class` object with an indeterminate full class name and return the first match successfully. + +This feature is usually used for class names in Android apps that are obfuscated by R8. + +> The following example + +```kotlin +// Assume that in version A, this class is com.example.a, +// In version B, this class is com.example.b. +val myClass = VariousClass("com.example.a", "com.example.b").load() +// You can also use a method with the suffix OrNull to return null +// instead of throwing an exception if Class is not found. +val myClassOrNull = VariousClass("com.example.a", "com.example.b").loadOrNull() +``` + +#### Lazy Loading Class Object + +KavaRef provides the `LazyClass` class to lazy loading the `Class` object. + +You can load `Class` when needed, instead of loading it immediately when created, +which can solve some `Class` that need to be loaded when run or run to specific conditions. + +> The following example + +```kotlin +// Define a Class that cannot be loaded for null and hosts it to myClass. +val myClass by lazyClass("com.example.MyClass") +// Define a Class that can be loaded for null delay and host it to myClassOrNull. +val myClassOrNull by lazyClassOrNull("com.example.MyClass") +// It can also support incoming VariousClass. +val otherClassOrNull by lazyClassOrNull(VariousClass("com.example.a", "com.example.b")) +// Called and loaded when needed. +myClass.resolve() +myClassOrNull?.resolve() +otherClassOrNull?.resolve() +``` + +#### ClassLoader Extensions + +KavaRef also provides some practical extension methods for `ClassLoader`. + +> The following example + +```kotlin +// Assume that's your ClassLoader. +val classLoader: ClassLoader +// Load a Class and return null if the load fails. +val myClassOrNull = classLoader.loadClassOrNull("com.example.MyClass") +// Determine whether this Class exists in the current ClassLoader. +val isClassExists = classLoader.hasClass("com.example.MyClass") +``` + +### Array Class Extensions + +In Java, the `Class` object of an array is a special `Class` object, and usually we create it as follows. + +For example, create a `Class` object of `java.lang.String[]`. + +> The following example + +```kotlin +val arrayClass = java.lang.reflect.Array.newInstance(String::class.java, 0).javaClass +``` + +This is very long to write and is not convenient to maintain, so KavaRef provides a way to simplify this process. + +Now, the `Class` object that creates `java.lang.String[]` can be written like this. + +> The following example + +```kotlin +val arrayClass = ArrayClass(String::class) +``` + +### Member Extensions + +KavaRef provides some extension methods to simplify operations on `Member`. + +You can set its accessibility using the `makeAccessible` method on any `Member` object. + +It will take effect if `Member` is the `AccessibleObject` type. + +> The following example + +```kotlin +// Suppose this is your current Member object. +val method: Method +// Make method is accessible. +method.makeAccessible() +``` + +Similarly, KavaRef also extends `Modifier`, and you can directly use `Member.isPublic` and other methods to judge a `Member` modifier. + +### Type Extensions + +When manipulating types or generic types in Java, you usually need to use the `Type` interface and its subinterface to handle it. + +KavaRef provides some extension methods to simplify operations on `Type`. + +For example, you can convert a `Type` that meets the requirements to a `Class` object. + +> The following example + +```kotlin +val type: Type +val clazz = type.toClass() +// You can also use a method with the suffix named OrNull to +// return null when the conversion fails instead of throwing an exception. +val clazzOrNull = type.toClassOrNull() +``` + +You can also convert `Type` that meets the requirements to `ParameterizedType` object. + +> The following example + +```kotlin +val type: Type +val parameterizedType = type.asParameterizedType() +// You can also use a method with the suffix named OrNull to +// return null when the conversion fails instead of throwing an exception. +val parameterizedTypeOrNull = type.asParameterizedTypeOrNull() +``` + +You can also use the following method to get the generic parameter array in the superclass, +which is often used in some superclass and subclass encapsulation operations. + +> The following example + +```kotlin +val myClass: Class<*> +// Get the generic parameter array of myClass superclass. +// If the acquisition fails or cannot be retrieved, the empty array will be returned. +val arguments = myClass.genericSuperclassTypeArguments() +``` + +### Java Wrapper Classes Extensions + +In Kotlin, you can directly use `Boolean::class`, `Byte::class`, etc. to obtain Java's original types `boolean` and `byte` instead of their wrapper classes. + +If you need to get Java wrapper classes, you need to use the complete `java.lang.Boolean::class`, `java.lang.Byte::class`, etc. +or use `Boolean::class.javaObjectType`, `Byte::class.javaObjectType`. + +So, KavaRef provides some type alias to handle Java wrapper classes. Now you only need to prefix `J` to these types, such as `JBoolean::class`. +It is equivalent to `java.lang.Boolean::class`, and some types need to be filled in the full name, such as `JInteger::class`. \ No newline at end of file diff --git a/docs-source/src/index.md b/docs-source/src/index.md new file mode 100644 index 0000000..e45ef78 --- /dev/null +++ b/docs-source/src/index.md @@ -0,0 +1,17 @@ +--- +home: true +navbar: false +sidebar: false +title: null +heroAlt: null +heroText: null +tagline: Select a language +actions: + - text: English + link: /en/ + type: secondary + - text: 简体中文 + link: /zh-cn/ + type: secondary +footer: Apache-2.0 License | Copyright (C) 2019 HighCapable +--- \ No newline at end of file diff --git a/docs-source/src/zh-cn/about/about.md b/docs-source/src/zh-cn/about/about.md new file mode 100644 index 0000000..23fbe87 --- /dev/null +++ b/docs-source/src/zh-cn/about/about.md @@ -0,0 +1,27 @@ +# 关于此文档 + +> 此文档由 [VuePress](https://v2.vuepress.vuejs.org/zh) 强力驱动。 + +## 许可证 + +[Apache-2.0](https://github.com/HighCapable/KavaRef/blob/main/LICENSE) + +```:no-line-numbers +Apache License Version 2.0 + +Copyright (C) 2019 HighCapable + +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 + + https://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. +``` + +版权所有 © 2019 HighCapable \ No newline at end of file diff --git a/docs-source/src/zh-cn/about/changelog.md b/docs-source/src/zh-cn/about/changelog.md new file mode 100644 index 0000000..5b87d98 --- /dev/null +++ b/docs-source/src/zh-cn/about/changelog.md @@ -0,0 +1,21 @@ +# 更新日志 + +> 这里记录了 `KavaRef` 的版本更新历史。 + +::: danger + +我们只会对最新的 API 版本进行维护,若你正在使用过时的 API 版本则代表你自愿放弃一切维护的可能性。 + +::: + +## kavaref-core + +### 1.0.0 | 2025.06.25   + +- 首个版本提交至 Maven + +## kavaref-extension + +### 1.0.0 | 2025.06.25   + +- 首个版本提交至 Maven \ No newline at end of file diff --git a/docs-source/src/zh-cn/about/contacts.md b/docs-source/src/zh-cn/about/contacts.md new file mode 100644 index 0000000..2b66e8b --- /dev/null +++ b/docs-source/src/zh-cn/about/contacts.md @@ -0,0 +1,15 @@ +# 联系我们 + +> 如在使用中有任何问题,或有任何建设性的建议,都可以联系我们。 + +加入我们的开发者群组。 + +- [点击加入 Telegram 群组](https://t.me/KavaRef) +- [点击加入 Telegram 群组 (开发者)](https://t.me/HighCapable_Dev) +- [点击加入 QQ 群 (开发者)](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf) + +在 **酷安** 找到我 [@星夜不荟](http://www.coolapk.com/u/876977)。 + +## 助力维护 + +感谢您选择并使用 `KavaRef`,如有代码相关的建议和请求,可在 GitHub 提交 Pull Request。 \ No newline at end of file diff --git a/docs-source/src/zh-cn/about/future.md b/docs-source/src/zh-cn/about/future.md new file mode 100644 index 0000000..6cd7147 --- /dev/null +++ b/docs-source/src/zh-cn/about/future.md @@ -0,0 +1,113 @@ +# 展望未来 + +> 未来是美好的,也是不确定的,让我们共同期待 `KavaRef` 在未来的发展空间。 + +## 未来的计划 + +> 这里收录了 `KavaRef` 可能会在后期添加的功能。 + +### 支持通过 ClassLoader 过滤 Class + +`KavaRef` 目前仅支持 `Method`、`Field`、`Constructor` 等反射 API 的查找和调用, +未来可能会根据需求在 Java 与 Android 平台支持通过指定类型的 `ClassLoader` 过滤 `Class` 的功能。 + +目前,你可以使用 [DexKit](https://github.com/LuckyPray/DexKit) 来完成这一需求,它同时支持更加复杂的 Method、Field、Constructor 等反射 API 的查找和调用。 + +### 自动生成反射代码 + +**这是在 [YukiReflection](https://github.com/HighCapable/YukiReflection) 中已经初步立项的功能,`KavaRef` 准备在未来可能的时间里继续实现它。** + +使用 `stub` 的方式创建一个 Kotlin 类,并声明其中的参数,以及其在各个版本中的不同状态。 + +比如下面的这个 Java 类就是我们需要反射的目标类。 + +> 示例如下 + +```java:no-line-numbers +package com.example.test; + +public class MyClass { + + private String myField = "test"; + + public MyClass() { + // ... + } + + private String myMethod1(String var1, int var2) { + // ... + } + + private void myMethod2() { + // ... + } + + private void myMethod3(String var1) { + // ... + } +} +``` + +通过目前 API 的现有用法可以使用如下方式反射调用这个类。 + +> 示例如下 + +```kotlin +MyClass().resolve().apply { + // 调用 myField + val value = firstField { name = "myField" }.get() + // 调用 myMethod1 + val methodValue = firstMethod { name = "myMethod1" }.invoke("test", 0) + // 调用 myMethod2 + firstMethod { name = "myMethod2" }.invoke() + // 调用 myMethod3 + firstMethod { name = "myMethod3" }.invoke("test") +} +``` + +目前要实现的功能是可以使用反射功能直接定义为如下 Kotlin 类。 + +> 示例如下 + +```kotlin +package com.example.test + +@ReflectClass +class MyClass { + + @ReflectField + val myField: String = fieldValueOf("none") + + @ReflectMethod + fun myMethod1(var1: String, var2: Int): String = methodReturnValueOf("none") + + @ReflectMethod + fun myMethod2() = MethodReturnType.Unit + + @ReflectMethod + fun myMethod3(var1: String) = MethodReturnType.Unit +} +``` + +然后我们就可以直接调用这个定义好的 Kotlin 类来实现反射功能,API 会根据注解自动生成反射代码。 + +> 示例如下 + +```kotlin +MyClass().also { + // 调用 myField + val value = it.myField + // 调用 myMethod1 + val methodValue = it.myMethod1("test", 0) + // 调用 myMethod2 + it.myMethod2() + // 调用 myMethod3 + it.myMethod3("test") +} +``` + +::: tip + +以上功能可能会在实际推出后有所变化,最终以实际版本的功能为准。 + +::: \ No newline at end of file diff --git a/docs-source/src/zh-cn/config/migration.md b/docs-source/src/zh-cn/config/migration.md new file mode 100644 index 0000000..280210a --- /dev/null +++ b/docs-source/src/zh-cn/config/migration.md @@ -0,0 +1,243 @@ +# 迁移至 KavaRef + +如果你已经习惯使用 [YukiReflection](https://github.com/HighCapable/YukiReflection) 或 [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI) 中的反射 API,你可以参考以下内容来迁移至 `KavaRef`。 + +::: warning + +针对 `YukiHookAPI`,你需要继续使用其 Hook API,`KavaRef` 仅包含 Java 反射相关 API。 + +::: + +## 基本功能 + +`KavaRef` 的设计理念与 `YukiReflection` 类似,但不完全相同,以下内容列举了 `YukiReflection` 与 `KavaRef` 在基本反射功能上的差异,你可以根据这些差异手动进行迁移。 + +例如我们有以下 Java 类。 + +> 示例如下 + +```java :no-line-numbers +public class MyClass { + + private static String content = "Hello, World!"; + + private void myMethod(String content) { + System.out.println("Hello " + content + "!"); + } +} +``` + +以下是 `KavaRef` 与 `YukiReflection` 的使用示例对比。 + +> 示例如下 + +
+ +
+

KavaRef

+ +```kotlin +// 假设这就是你的 MyClass 实例 +val myClass: MyClass +// 使用 KavaRef 调用并执行 +MyClass::class.resolve().firstMethod { + name = "myMethod" + parameters(String::class) +}.of(myClass).invoke("Hello, KavaRef!") +// 直接引用实例方式 +myClass.resolve().firstMethod { + name = "myMethod" + parameters(String::class) +}.invoke("Hello, KavaRef!") +``` + +
+ +
+

YukiReflection

+ +```kotlin +// 假设这就是你的 MyClass 实例 +val myClass: MyClass +// 使用 YukiReflection 调用并执行 +MyClass::class.java.method { + name = "myMethod" + param(StringClass) +}.get(myClass).call("Hello, YukiReflection!") +// 直接引用实例方式 +myClass.current().method { + name = "myMethod" + param(StringClass) +}.call("Hello, YukiReflection!") +``` + +
+
+ +`KavaRef` 在任何时候开始反射都需要使用 `resolve()` 来创建反射作用域,不再对 `Class` 等实例直接进行扩展相关 `method`、`constructor` 方法以避免污染其作用域。 + +`KavaRef` 抛弃了 "Finder" 的设计理念,使用 "Filter" (过滤器) 的设计理念来获取反射结果,“查找” 不再是查找,而是 “过滤”。 + +`KavaRef` 取消了 `YukiReflection` 在结果实例中定义获取的 `Member` 为多重还是单一的设计方案,直接返回整个 `List`, +你在上方看到的示例使用了 `firstMethod` 来获取第一个匹配的 `MethodResolver`,如果你需要获取所有匹配的结果,可以改为 `method`。 + +`KavaRef` 在 `MethodCondition` 中的条件方法名称已由 `YukiReflection` 之前的 `param` 等简写修改为 `parameters`,以更符合 Java 反射 API 的命名习惯。 + +`KavaRef` 不再提供条件中的 `param(...).order()` 功能,因为这个功能本身就不稳定,`KavaRef` 现在使用迭代器进行过滤操作,字节码将不再有顺序,且本不应该使用顺序筛选字节码,你可以使用 `firstMethod`、`firstField` 或 `lastMethod`、`lastField` 等方法来获取第一个或最后一个匹配的结果。 + +`KavaRef` 将 `get(instance)` 方法更名为 `of(instance)`,因为 `get(...)` 可能会与 `Field` 的 `get(...)` 用法产生混淆且语义不明确, +同时 `get(instance)` 也不再是从类似 `MethodFinder.Result` 来获取 `MethodFinder.Result.Instance` 实例,而是使用 `of(instance)` 来始终操作和设置实例对象到 `MemberResolver`。 + +类似 `MethodFinder.Result.Instance` 中的 `string()`、`int()` 等方法在 `KavaRef` 中已被移除, +你可以直接使用 `get()`、`get()`、`invoke(...)`、`invoke(...)` 等方式来获取或调用对应类型的结果。 + +::: danger + +如果你正在查找 (过滤) `Field`,你需要注意 `KavaRef` 与 `YukiReflection` 在 `Field` 的获取方式上有可能会发生语义冲突,在迁移这部分的时候请特别注意。 + +例如获取 `MyClass` 中的 `content` 静态字段,在 `YukiReflection` 中,你会这样做。 + +> 示例如下 + +```kotlin +MyClass::class.java + .field { name = "content" } // 返回 FieldFinder.Result + .get() // 不可省略,返回 FieldFinder.Result.Instance + .string() // 值 +``` + +在 `KavaRef` 中,你需要这样做。 + +> 示例如下 + +```kotlin +MyClass::class.resolve() + .firstField { name = "content" } // 返回 FieldResolver + .get() // 值 +``` + +正如上面所说,`get(...)` 在 `YukiReflection` 中是获取 `FieldFinder.Result.Instance` 对象,而不是值,要获取值并处理为指定类型,你需要调用 `string()` 或者 `cast()`,而在 `KavaRef` 中是在 `MemberResolver` 中直接使用 `get()` 来获取指定类型的值,`KavaRef` 对应 `YukiReflection` 的 `get(...)` 的用法是 `of(...)`。 + +所以上述示例在 `KavaRef` 中的完整写法应该为。 + +> 示例如下 + +```kotlin +// 由于调用的是静态实例,"of(null)" 可被省略 +MyClass::class.resolve() + .firstField { name = "content" } // 已是调用链对象 FieldResolver + .of(null) // 可省略,返回调用链对象 FieldResolver + .get() // 值 +``` + +::: + +`KavaRef` 不再对 `Method` 提供 `call` 方法,现在统一合并为 `invoke` (带泛型参数),同时 `KavaRef` 将 `Constructor` 的 `newInstance` 方法定义为 `create` (带泛型参数)。 + +你可能注意到条件 `superClass()` 消失了,它还在,在 `KavaRef` 中它已更名为 `superclass()`,对接标准的 Java 反射 API。 + +同时,`KavaRef` 对 `KClass` 进行了扩展,你不再需要在大部分场景中使用 `Some::class.java` 的方式来声明一个 `Class` 实例。 + +`KavaRef` 的另一个设计思想就是类型安全,只要是你在使用声明指定泛型类型的 `KClass`、`Class` 时,在 `of(instance)`、`create(...)` 时都会校验、转换为对应类型,在编码期间就完成类型检查,避免运行时错误。 + +> 示例如下 + +```kotlin +// 假设这就是你的 MyClass 实例 +val myClass: MyClass +// 使用 KavaRef 调用并执行 +MyClass::class + .resolve() + .firstMethod { + name = "myMethod" + parameters(String::class) + } + // 只能传入 MyClass 类型的实例 + .of(myClass) + .invoke("Hello, KavaRef!") +``` + +## 其它功能 + +`KavaRef` 与 `YukiReflection` 在其它功能及扩展功能中的实现差异不大,`KavaRef` 将这些功能单独分离为了一个独立的模块。 + +以下功能在 `YukiReflection` 中提供,但在 `KavaRef` 中没有实现且不再提供: + +- 预置反射类型常量类,如 `StringClass`、`IntType` 等 + - 你可以直接使用 `String::class`、`Int::class` 等 Kotlin 的类引用进行替代,对于原始类型与包装类,`IntType` 等价于 `Int::class`,`IntClass` 等价于 `JInteger::class` + +- `DexClassFinder` 功能 + - 由于其设计缺陷,且在 Android 平台上使用时可能存在性能问题,目前不再提供 + +- `RemedyPlan` 和 `method { ... } .remedys { ... }` 功能 + - 由于此功能存在可能的黑盒问题,维护相对困难,如需使用类似功能,请手动实现,不再提供 + +- `ClassLoader.listOfClasses()` 功能 + - 由于各个平台实现方案复杂且不稳定,不再提供 + +- `ClassLoader.searchClass()` 功能 + - 由于性能问题,且设计时仅限于 Android 平台使用,过滤条件维护相对困难,不再提供 + +- `Class.hasExtends`、`Class.extends`、`Class.implements` 功能 + - 你可以使用 `A::class isSubclassOf B::class` 来取代它们 + +- `Class.toJavaPrimitiveType()` 功能 + - 功能设计上存在概念混淆问题,不再提供 + +- `"com.some.clazz".hasClass(loader)` 功能 + - 你可以使用 `loader.hasClass("com.some.clazz")` 来取代它 + +- `Class.hasField`、`Class.hasMethod`、`Class.hasConstructor` 功能 + - 由于设计缺陷,不再提供 + +- `Class.hasModifiers(...)`、`Member.hasModifiers(...)` 功能 + - 你可以直接使用 `Class.isPublic`、`Member.isPublic` 等扩展方法来取代它们。 + +- `Class.generic()`、`GenericClass` 功能 + - 如果只是希望获取超类的泛型参数,你可以使用 `Class.genericSuperclassTypeArguments()`,由于设计缺陷,不再提供 + +- `Class.current()`、`CurrentClass` 功能 + - 已合并到 `KavaRef.resolve()` 的核心功能中,不再单独提供 + +- `Class.buildOf(...)` 功能 + - 你可以使用 `Class.createInstance(...)` 来取代它 + +- `Class.allMethods()`、`Class.allFields()`、`Class.allConstructors()` 功能 + - 由于其污染作用域,不再提供 + +- `YLog` 日志功能 + - `KavaRef` 不再接管日志,你可以使用对应平台的实现方式,不再提供 + +## 异常处理 + +`KavaRef` 在异常处理方面与 `YukiReflection` 完全不同,`KavaRef` 的异常逻辑将保持默认透明,**它不再主动拦截异常并打印错误日志甚至是提供 `onNoSuchMethod` 监听**,当没有过滤到任何有效的成员时,`KavaRef` 会直接抛出异常,除非你**明确声明条件为可选 (与 `YukiReflection` 逻辑保持一致)**。 + +> 示例如下 + +```kotlin +// 假设这就是你的 MyClass 实例 +val myClass: MyClass +// 使用 KavaRef 调用并执行 +MyClass::class + .resolve() + .optional() // 声明为可选,不要抛出异常 + // 使用 firstMethodOrNull 替代 firstMethod,因为找不到会抛出 Kotlin 自身的 NoSuchElementException + .firstMethodOrNull { + name = "doNonExistentMethod" // 假设这个方法不存在 + parameters(String::class) + }?.of(myClass)?.invoke("Hello, KavaRef!") +``` + +更多内容请参考 [kavaref-core](../library/kavaref-core.md) 中的 [异常处理](../library/kavaref-core.md#异常处理) 部分。 + +## 初次使用 KavaRef + +如果你没用过 `YukiReflection` 或者 `YukiHookAPI`,没关系,你可以参考以下内容来快速上手。 + +::: tip 接下来做什么 + +更多内容,请继续阅读 [kavaref-core](../library/kavaref-core.md) 和 [kavaref-extension](../library/kavaref-extension.md)。 + +立即开始使用 `KavaRef` 吧! + +::: \ No newline at end of file diff --git a/docs-source/src/zh-cn/config/processor-resolvers.md b/docs-source/src/zh-cn/config/processor-resolvers.md new file mode 100644 index 0000000..627fa3f --- /dev/null +++ b/docs-source/src/zh-cn/config/processor-resolvers.md @@ -0,0 +1,38 @@ +# 第三方 Member 解析器 + +> 这里收录了一些第三方的 Member 解析器,可供参考与使用。 +> +> 使用方法请阅读 [自定义解析器](../library/kavaref-core.md#自定义解析器)。 + +## AndroidHiddenApiBypass + +[项目地址](https://github.com/LSPosed/AndroidHiddenApiBypass) + +> LSPass: Bypass restrictions on non-SDK interfaces + +```kotlin +class AndroidHiddenApiBypassResolver : MemberProcessor.Resolver() { + + override fun getDeclaredConstructors(declaringClass: Class): List> { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + return super.getDeclaredConstructors(declaringClass) + } + + val constructors = HiddenApiBypass.getDeclaredMethods(declaringClass) + .filterIsInstance>() + .toList() + return constructors + } + + override fun getDeclaredMethods(declaringClass: Class): List { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + return super.getDeclaredMethods(declaringClass) + } + + val methods = HiddenApiBypass.getDeclaredMethods(declaringClass) + .filterIsInstance() + .toList() + return methods + } +} +``` \ No newline at end of file diff --git a/docs-source/src/zh-cn/config/r8-proguard.md b/docs-source/src/zh-cn/config/r8-proguard.md new file mode 100644 index 0000000..b24dc13 --- /dev/null +++ b/docs-source/src/zh-cn/config/r8-proguard.md @@ -0,0 +1,5 @@ +# R8 与 Proguard 混淆 + +> 大部分场景下 Android 应用程序安装包可通过混淆压缩体积,这里介绍了混淆规则的配置方法。 + +`KavaRef` 在 Android 项目中不需要额外配置任何混淆规则。 \ No newline at end of file diff --git a/docs-source/src/zh-cn/guide/home.md b/docs-source/src/zh-cn/guide/home.md new file mode 100644 index 0000000..d55aa94 --- /dev/null +++ b/docs-source/src/zh-cn/guide/home.md @@ -0,0 +1,36 @@ +# 介绍 + +> `KavaRef` 是一个使用 Kotlin 实现的现代化 Java 反射 API。 + +## 背景 + +这是一个使用 Kotlin 实现的现代化 Java 反射 API,旨在提供更简洁、更易用的 API,同时保留 Java 反射的强大功能。 + +项目图标由 [MaiTungTM](https://github.com/Lagrio) 设计,名称取自 **K**otlinJ**avaRef**lection,意为使用 Kotlin 实现的 Java 反射。 + +它最早诞生于 [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI),后期被解耦合为 [YukiReflection](https://github.com/HighCapable/YukiReflection) 项目。 + +如你所见,现在 `KavaRef` 是借助 `YukiReflection` 的设计思想完全重构的一套全新 API,它们没有从属关系,并将取代 `YukiReflection` 成为一个全新的反射解决方案。 + +如果你正在使用 `YukiReflection` 或与之相关的 `YukiHookAPI` 项目,你可以参考 [这里](../config/migration) 来迁移反射 API 的写法到 `KavaRef`。 + +## 用途 + +`KavaRef` 采用 Kotlin **lambda** 语法与 Java Builder 风格构建。 + +它能取代 [Java 原生的反射 API](https://pdai.tech/md/java/basic/java-basic-x-reflection.html),使用更加人性化的语言实现一套更加完善的反射方案。 + +## 技能要求 + +你必须已熟练掌握 Java 原生的反射 API,了解 Java 的类加载机制、字节码结构以及它们在 Kotlin 中的用法 (如果你正在使用 Kotlin)。 + +## 语言要求 + +推荐使用 Kotlin,API 代码构成同样支持 Java,但是在纯 Java 项目中 `KavaRef` 有可能无法发挥其全部功能和语法糖优势。 + +文档全部的 Demo 示例代码都将首先使用 Kotlin 进行描述,如果你完全不会使用 Kotlin 那你将有可能无法更全面地体验和使用 `KavaRef` 的功能。 + +## 功能贡献 + +本项目的维护离不开各位开发者的支持和贡献,目前这个项目处于初期阶段,可能依然存在一些问题或者缺少你需要的功能, +如果可能,欢迎提交 PR 为此项目贡献你认为需要的功能或前往 [GitHub Issues](repo://issues) 向我们提出建议。 \ No newline at end of file diff --git a/docs-source/src/zh-cn/guide/quick-start.md b/docs-source/src/zh-cn/guide/quick-start.md new file mode 100644 index 0000000..76ee6c6 --- /dev/null +++ b/docs-source/src/zh-cn/guide/quick-start.md @@ -0,0 +1,95 @@ +# 快速开始 + +> 集成 `KavaRef` 到你的项目中。 + +## 项目要求 + +项目需要使用 `IntelliJ IDEA` 或 `Android Studio` 创建且类型为 Java 或 Android 项目并已集成 Kotlin 环境依赖。 + +- IntelliJ IDEA (建议 [从这里](https://www.jetbrains.com/idea) 获取最新版本) + +- Android Studio (建议 [从这里](https://developer.android.com/studio) 获取最新版本) + +- Kotlin 1.9.0+、Gradle 8+、Java 17+ + +### 配置存储库 + +`KavaRef` 的依赖发布在 **Maven Central** 和我们的公共存储库中,你可以使用如下方式配置存储库。 + +我们推荐使用 Kotlin DSL 作为 Gradle 构建脚本语言并推荐使用 [SweetDependency](https://github.com/HighCapable/SweetDependency) 来管理依赖。 + +#### SweetDependency (推荐) + +在你的项目 `SweetDependency` 配置文件中配置存储库。 + +```yaml +repositories: + google: + maven-central: + # (可选) 你可以添加此 URL 以使用我们的公共存储库 + # 当 Sonatype-OSS 发生故障无法发布依赖时,此存储库作为备选进行添加 + # 详情请前往:https://github.com/HighCapable/maven-repository + highcapable-maven-releases: + # 中国大陆用户请将下方的 "raw.githubusercontent.com" 修改为 "raw.gitmirror.com" + url: https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases +``` + +#### 传统方式 + +在你的项目 `build.gradle.kts` 中配置存储库。 + +```kotlin +repositories { + google() + mavenCentral() + // (可选) 你可以添加此 URL 以使用我们的公共存储库 + // 当 Sonatype-OSS 发生故障无法发布依赖时,此存储库作为备选进行添加 + // 详情请前往:https://github.com/HighCapable/maven-repository + // 中国大陆用户请将下方的 "raw.githubusercontent.com" 修改为 "raw.gitmirror.com" + maven("https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases") +} +``` + +### 配置 Java 版本 + +在你的项目 `build.gradle.kts` 中修改 Kotlin 的 Java 版本为 17 及以上。 + +> Java 项目 + +```kt +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +kotlin { + jvmToolchain(17) +} +``` + +> Android 项目 + +```kt +android { + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} +``` + +## 功能一览 + +整个项目分为多个模块,你可以选择你希望引入的模块作为依赖应用到你的项目中,但一定要包含 **kavaref-core** 模块。 + +你可以点击下方对应的模块前往查看详细的功能介绍。 + +- [kavaref-core](../library/kavaref-core.md) +- [kavaref-extension](../library/kavaref-extension.md) + +## Demo + +你可以在 [这里](repo://tree/main/samples) 找到一些示例,查看对应的演示项目来更好地了解这些功能的运作方式,快速地挑选出你需要的功能。 \ No newline at end of file diff --git a/docs-source/src/zh-cn/index.md b/docs-source/src/zh-cn/index.md new file mode 100644 index 0000000..257b435 --- /dev/null +++ b/docs-source/src/zh-cn/index.md @@ -0,0 +1,38 @@ +--- +home: true +title: 首页 +heroImage: /images/logo.svg +actions: + - text: 快速上手 + link: /zh-cn/guide/home + type: primary + - text: 更新日志 + link: /zh-cn/about/changelog + type: secondary +features: + - title: 轻量优雅 + details: 拥有一套强大、优雅、人性化、完全使用 Kotlin lambda 打造的 API,可以帮你快速实现字节码的过滤以及反射功能。 + - title: 全面兼容 + details: 使用原生 Java API 实现反射功能,可在任何 Kotlin on JVM 的项目上使用,在 Android 上使用也丝毫不成问题。 + - title: 快速上手 + details: 简单易用,不需要繁琐的配置,不需要十足的开发经验,搭建环境集成依赖即可立即开始使用。 +footer: Apache-2.0 License | Copyright (C) 2019 HighCapable +--- + +### 随时随地,开始反射。 + +```java +public class World { + + private void sayHello(String content) { + System.out.println("Hello " + content + "!"); + } +} +``` + +```kotlin +World().resolve().firstMethod { + name = "sayHello" + parameters(String::class) +}.invoke("KavaRef") +``` \ No newline at end of file diff --git a/docs-source/src/zh-cn/library/kavaref-core.md b/docs-source/src/zh-cn/library/kavaref-core.md new file mode 100644 index 0000000..125e637 --- /dev/null +++ b/docs-source/src/zh-cn/library/kavaref-core.md @@ -0,0 +1,768 @@ +# kavaref-core + +![Maven Central](https://img.shields.io/maven-central/v/com.highcapable.kavaref/kavaref-core?logo=apachemaven&logoColor=orange&style=flat-square) + +![Maven metadata URL](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fraw.githubusercontent.com%2FHighCapable%2Fmaven-repository%2Frefs%2Fheads%2Fmain%2Frepository%2Freleases%2Fcom%2Fhighcapable%2Fkavaref%2Fkavaref-core%2Fmaven-metadata.xml&logo=apachemaven&logoColor=orange&label=highcapable-maven-releases&style=flat-square) + +这是 KavaRef 的核心依赖,你需要引入此模块才能使用 KavaRef 的基本功能。 + +## 配置依赖 + +你可以使用以下方式将此模块添加到你的项目中。 + +### SweetDependency (推荐) + +在你的项目 `SweetDependency` 配置文件中添加依赖。 + +```yaml +libraries: + com.highcapable.kavaref: + kavaref-core: + version: + +``` + +在你的项目 `build.gradle.kts` 中配置依赖。 + +```kotlin +implementation(com.highcapable.kavaref.kavaref.core) +``` + +### Version Catalog + +在你的项目 `gradle/libs.versions.toml` 中添加依赖。 + +```toml +[versions] +kavaref-core = "" + +[libraries] +kavaref-core = { module = "com.highcapable.kavaref:kavaref-core", version.ref = "kavaref-core" } +``` + +在你的项目 `build.gradle.kts` 中配置依赖。 + +```kotlin +implementation(libs.kavaref.core) +``` + +请将 `` 修改为此文档顶部显示的版本。 + +### 传统方式 + +在你的项目 `build.gradle.kts` 中配置依赖。 + +```kotlin +implementation("com.highcapable.kavaref:kavaref-core:") +``` + +请将 `` 修改为此文档顶部显示的版本。 + +## 功能介绍 + +你可以 [点击这里](kdoc://kavaref-core) 查看 KDoc。 + +### 基本用法 + +KavaRef 采用链式调用的设计方案,它对可用的 Java 反射 API (例如 `Class`) 创建了扩展方法,你只需要对这些内容调用 `resolve()`,即可进入 KavaRef 的世界。 + +关系图如下。 + +``` :no-line-numbers +KavaRef +└── KClass/Class/Any.resolve() + ├── method() + ├── constructor() + └── field() +``` + +接下来,我们将给出多个示例的 Java `Class`,后续都将基于它们进行基本的反射方案讲解。 + +```java :no-line-numbers +package com.demo; + +public class BaseTest { + + public BaseTest() { + // ... + } + + private void doBaseTask(String taskName) { + // ... + } +} +``` + +```java :no-line-numbers +package com.demo; + +public class Test extends BaseTest { + + private Test() { + // ... + } + + private static TAG = "Test"; + + private boolean isTaskRunning = false; + + private void doTask(String taskName) { + // ... + } + + private void release(String taskName, Function task, boolean isFinish) { + // ... + } + + private void stop() { + // ... + } + + private String getName() { + // ... + } +} +``` + +```java +public class Box { + + public void print(T item, String str) { + // ... + } +} +``` + +假设,我们想要得到 `Test` 的 `doTask` 方法并执行,在 KavaRef 中,你可以通过以下方式来实现。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 通过实例化 Class 的方式对其进行反射 +// 在 KavaRef 中,你无需将其转换为 `java.lang.Class`, +// 它会自动调用 KClass.java +Test::class + // 创建 KavaRef 反射 + .resolve() + // 创建 Method (方法) 条件 + .method { + // 设置方法名 + name = "doTask" + // 设置方法参数类型 + parameters(String::class) + } + // 条件执行后会返回匹配到的 List 实例 + // 这里我们获取到过滤结果的第一个 + .first() + // 在 MethodResolver 上设置 Test 的实例 + .of(test) + // 调用方法并传入参数 + .invoke("task_name") +``` + +在以上写法中,我们通过 `Test::class.resolve()` 来获取当前 `Class` 的 KavaRef 反射实例, +然后通过 `method { ... }` 来创建一个方法过滤条件 `MethodCondition`,在其中设置方法名和参数类型,执行后返回 `List` 实例, +接着我们通过 `first()` 来获取第一个匹配到的 `MethodResolver` 实例, +然后通过 `of(test)` 来设置当前 `Class` 的实例,最后通过 `invoke("task_name")` 来执行方法并传入参数。 + +在这其中,`MethodCondition` 继承自 `MemberCondition`,它允许你对 `Method` 进行条件筛选,其中包含了 Java 核心的反射 API 的条件镜像,你可以查看对应的注释来了解每个 API 的原生用法。 + +同样地,`MethodResolver` 继承自 `MemberResolver`,它允许你对过滤结果中的 `Method` 进行反射调用。 + +由于这里的反射需求是得到一个可用的方法结果,所以 `method { ... }.first()` 的调用链可能来起来会比较繁琐,这个时候就有以下简化方案。 + +> 示例如下 + +```kotlin +Test::class + .resolve() + // 直接使用 firstMethod 来获取第一个匹配到的 MethodResolver 实例 + .firstMethod { + name = "doTask" + parameters(String::class) + } + .of(test) + .invoke("task_name") +``` + +由于我们现在可以拿到 `Test` 的实例,那么还有一种简化写法,你可以直接使用这个实例创建 KavaRef 反射。 + +> 示例如下 + +```kotlin +// 在这里,Test 的实例 test 会被传给 KavaRef 并获取 test::class.java +test.resolve() + .firstMethod { + name = "doTask" + parameters(String::class) + } // 由于你设置了实例,所以这里不再需要 of(test) + .invoke("task_name") +``` + +接下来,我们需要得到 `isTaskRunning` 变量,可以写作以下形式。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 使用 KavaRef 调用并执行 +val isTaskRunning = test.resolve() + .firstField { + name = "isTaskRunning" + type = Boolean::class + }.get() +``` + +`Test` 中的构造方法是私有化的,现在,我们可以使用以下方式来创建它的实例。 + +> 示例如下 + +```kotlin +val test = Test::class.resolve() + .firstConstructor { + // 对于零参构造方法,可以使用以下条件过滤 + // 它等价于 parameterCount = 0 + emptyParameters() + }.create() // 创建一个新的 Test 实例 +``` + +你也可以使用 `createAsType()` 为实际对象 `Test` 指定其超类类型 `BaseTest`。 + +> 示例如下 + +```kotlin +val test = Test::class.resolve() + .firstConstructor { + emptyParameters() + }.createAsType() // 创建一个新的 BaseTest 实例 +``` + +::: tip + +除了 `firstMethod` 等方法外,你也可以使用 `lastMethod` 等方法来获取最后一个匹配到的 `MethodResolver` 实例,它等价于 `method { ... }.last()`。 + +在得到 `MemberResolver` 实例后,你可以使用 `self` 来获取当前 `MemberResolver` 的 `Member` 原始实例来对其进行一些你自己的操作。 + +在继承于 `InstanceAwareResolver` 的 `MemberResolver` 中 (例如 `MethodResolver` 和 `FieldResolver`),你都可以使用 `of(instance)` +来设置当前实例,如果反射得到的是静态 (static) 成员,你无需设置实例。 + +::: + +::: danger + +在继承于 `InstanceAwareResolver` 的 `MemberResolver` 中,`of(instance)` 的类型要求与当前反射的 `Class` 实例泛型类型相同, +除非不指定 `Class` 泛型类型,或将 `Class` 泛型类型设置为 `Any`。 + +如果 `of(instance)` 出现 `Required: Nothing?` 错误 (这通常由于 `Class` 通过 `Class.forName(...)` 或 `ClassLoader.loadClass(...)` 创建), +则是你的 `Class` 为 `Class<*>` (Java 中是 `Class`),此时如果你不想指定类型,请设置或转换为 `Class`,就像下面这样。 + +> 示例如下 + +```kotlin +val myClass = Class.forName("com.xxx.MyClass") as Class +// 假设这就是这个 Class 的实例 +val myClassInstance: Any +myClass.resolve() + .firstMethod { + // ... + }.of(myClassInstance).invoke(...) +``` + +你也可以使用 [kavaref-extension](kavaref-extension.md) 中提供的 [创建 Class 对象](kavaref-extension.md#创建-class-对象) 来解决这个问题。 + +::: + +### 模糊条件 + +你会注意到 `Test` 中有一个 `release` 方法,但是它的方法参数很长,而且部分类型可能无法直接得到。 + +此时,你可以借助 `parameters(...)` 条件使用 `VagueType` 来填充你不想填写的方法参数类型。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 使用 KavaRef 调用并执行 +test.resolve() + .firstMethod { + name = "release" + // 使用 VagueType 来填充不想填写的类型,同时保证其它类型能够匹配 + parameters(String::class, VagueType, Boolean::class) + } // 得到这个方法 +``` + +::: warning + +`VagueType` 只能在有多个参数的过滤条件时使用,它不可以在只能设置单个参数的过滤条件中使用,例如 `type`。 + +你可以使用 `VagueType`、`VagueType::class` 或 `VagueType::class.java` 来创建,它们都能被正确识别为模糊过滤条件。 + +::: + +### 自由条件 + +在 `MemberCondition` 中,`name`、`type`、`parameterCount` 等条件都可以使用 Kotlin lambda 特性创建自由过滤条件。 + +假设我们要得到 `Test` 中的 `doTask` 方法,可以使用以下实现。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 使用 KavaRef 调用并执行 +test.resolve() + .firstMethod { + // 使用 lambda 来设置方法名 + name { + // 设置名称不区分大小写 + it.equals("dotask", ignoreCase = true) + } + // 设置参数类型 + parameters(String::class) + }.invoke("task_name") +``` + +### 泛型条件 + +KavaRef 支持添加泛型过滤条件,你可以使用 `TypeMatcher` 提供的相关功能来实现。 + +假设我们需要过滤 `Box` 中的 `print` 方法。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val box: Box +// 使用 KavaRef 调用并执行 +box.resolve() + .firstMethod { + name = "print" + // 设置泛型参数条件 + genericParametes( + // 过滤泛型名称 "T" + typeVar("T"), + // 通过 Class 创建 TypeMatcher + String::class.toTypeMatcher() + ) + }.invoke("item", "str") +``` + +### 在超类过滤 + +你会注意到 `Test` 继承于 `BaseTest`,现在我们想得到 `BaseTest` 的 `doBaseTask` 方法。 + +在不知道超类名称的情况下,我们只需要在过滤条件中加入 `superclass()` 即可实现这个功能。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 使用 KavaRef 调用并执行 +test.resolve() + .firstMethod { + name = "doBaseTask" + parameters(String::class) + // 只需要添加这个条件 + superclass() + }.invoke("task_name") +``` + +这个时候我们就可以在超类中获取到这个方法了。 + +::: tip + +`superclass()` 一旦设置就会自动循环向后过滤全部继承的超类中是否有这个方法,直到过滤到目标没有超类 (继承关系为 `java.lang.Object`) 为止。 + +::: + +::: danger + +当前过滤的方法除非指定 `superclass()` 条件,否则只能过滤到当前 `Class` 的方法,这是 Java 反射 API 的默认行为, +KavaRef 会调用 `Class.getDeclaredMethods()` 来获取当前 `Class` 的方法而不是 `Class.getMethods()`。 + +::: + +### 更多条件 + +KavaRef 提供了一些过滤条件来辅助 Java 反射 API 的使用。 + +假设我们要得到 `Test` 中的静态变量 `TAG` 的内容。 + +为了体现过滤的条件包含静态描述符 (static),我们可以使用以下方式来实现。 + +> 示例如下 + +```kotlin +val tag = Test::class.resolve() + .firstField { + name = "TAG" + type = String::class + // 创建描述符过滤 + modifiers(Modifiers.STATIC) + // 或者 + modifiers { + it.contains(Modifiers.STATIC) + } + }.get() // 获取字段内容 +``` + +你还可以在 `type`、`parameters` 等条件中使用字符串类型传入完整类名。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 使用 KavaRef 调用并执行 +test.resolve() + .firstMethod { + name = "doTask" + // 使用字符串类型传入完整类名 + parameters("java.lang.String") + }.invoke("task_name") +``` + +### 异常处理 + +在默认情况下,KavaRef 会在反射调用过程中找不到成员时抛出异常。 + +> 示例如下 + +```kotlin +Test::class.resolve() + .method { + name = "doNonExistentMethod" + } // 这里会抛出 NoSuchMethodException +``` + +如果你不希望抛出异常,可以设置可选条件 `optional()`。 + +> 示例如下 + +```kotlin +Test::class.resolve() + // 设置可选条件 + .optional() + .method { + name = "doNonExistentMethod" + } // 返回空的 List +``` + +KavaRef 会打印完整的异常内容以供调试,在使用 `optional()` 时,异常会以 WARN 级别的日志打印。 + +> 示例如下 + +``` :no-line-numbers +No method found matching the condition for current class. ++------------------------------------------------+ +| class com.demo | ++------------+-----------------------------------+ +| name | doNonExistentMethod | +| parameters | [class java.lang.String, boolean] | ++------------+-----------------------------------+ +``` + +如果你不希望 KavaRef 抛出或打印任何内容,你可以使用 `optional(silent = true)` 静默化处理,但是我们**不建议这样做**,这会掩盖问题,除非有必要这么做。 + +::: danger + +如果你设置了 `optional()`,那么请不要使用 `firstMethod`、`firstConstructor` 等方法来获取单个结果, +因为它们会在没有结果时抛出列表为空的异常,你可以使用后缀为 `OrNull` 的方法来获取单个结果。 + +::: + +### 日志管理 + +KavaRef 提供了其自身的日志管理功能,你可以通过 `KavaRef.logLevel` 来设置日志级别。 + +你可以设置 `KavaRef.logLevel = KavaRefRuntime.LogLevel.DEBUG` 来启用 DEBUG 级别的日志使得 KavaRef 在过滤过程向控制台打印更为详细的分步过滤条件日志。 + +如果你想关闭 KavaRef 的全部日志打印,你可以设置 `KavaRef.logLevel = KavaRefRuntime.LogLevel.OFF`。 + +如果你有更高级的需求,你可以实现 `KavaRefRuntime.Logger` 来自定义自己的日志打印方式。 + +> 示例如下 + +```kotlin +class MyLogger : KavaRefRuntime.Logger { + + // 在这里可以指定日志打印的标签 + override val tag = "MyLogger" + + override fun debug(msg: Any?, throwable: Throwable?) { + // 在这里实现你的日志打印逻辑 + } + + override fun info(msg: Any?, throwable: Throwable?) { + // 在这里实现你的日志打印逻辑 + } + + override fun warn(msg: Any?, throwable: Throwable?) { + // 在这里实现你的日志打印逻辑 + } + + override fun error(msg: Any?, throwable: Throwable?) { + // 在这里实现你的日志打印逻辑 + } +} +``` + +然后,将其设置到 KavaRef 上即可。 + +> 示例如下 + +```kotlin +KavaRef.setLogger(MyLogger()) +``` + +### 进阶用法 + +上述内容讲解的均为标准场景下的使用方法,如果你有更加细粒度的使用场景,你可以手动创建 KavaRef 的相关组件。 + +如果你不喜欢 Kotlin lambda 的写法,你可以手动创建链式调用。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 使用 KavaRef 调用并执行 +test.resolve() + .method() // 条件开始 + .name("doTask") + .parameters(String::class) + .build() // 条件结束 (执行) + .first() + .invoke("task_name") +``` + +你还可以手动创建任何过滤条件以实现在任何反射中复用它。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 手动创建 MethodCondition +val condition = MethodCondition() +condition.name = "doTask" +condition.parameters(String::class) +// 应用条件到反射对象 +Test::class.resolve() + .firstMethod(condition) + .of(test) // 设置实例 + .invoke("task_name") +``` + +或者,你还可以手动完整地实现整个反射过程。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 手动创建 MethodCondition +val condition = MethodCondition() +condition.name = "doTask" +condition.parameters(String::class) +// 手动创建 MemberCondition.Configuration +val configuration = Test::class.java.createConfiguration( + memberInstance = test, // 设置实例 + processorResolver = null, // 使用默认的解析器,可参考下方的 "自定义解析器" + superclass = false, // 是否在超类中过滤 + optional = MemberCondition.Configuration.Optional.NO // 配置可选条件 +) +// 创建并开始过滤 +val resolvers = condition.build(configuration) +// 获取第一个结果 +val resolver = resolvers.first() +// 执行方法 +resolver.invoke("task_name") +``` + +如果你对业务层逻辑有更高级的需求,你还可以使用 `mergeWith` 来合并多个过滤条件。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 手动创建 MethodCondition +// 创建第一个条件 +val condition1 = MethodCondition() +condition1.name = "doTask" +// 创建第二个条件 +val condition2 = MethodCondition() +condition2.parameters(String::class) +// 将 condition2 合并到 condition1 中 +// 此时 condition1 的条件将包含 condition2 不为 null 的条件, +// condition1 中重复的条件将被 condition2 的条件覆盖 +condition1.mergeWith(condition2) +// 你还可以使用 infix 语法 +condition1 mergeWith condition2 +// 使用 KavaRef 调用并执行 +Test::class.resolve() + .firstMethod(condition1) + .of(test) + .invoke("task_name") +``` + +::: danger + +当 `MemberCondition` 已经设置 `MemberCondition.Configuration` 时将不再允许重复使用 `build(...)` 进行创建, +此时你需要使用 `copy()` 来复制并创建一份新的 `MemberCondition`。 + +同样地,`InstanceAwareResolver` 在通过 `MemberCondition.Configuration.memberInstance` 或 `of(instance)` 设置实例后也不允许重复设置新的实例, +此时你也需要使用 `copy()` 来复制并创建一份新的 `InstanceAwareResolver`。 + +::: + +### 自定义解析器 + +KavaRef 使用默认的 `Member` 解析器进行过滤操作,如果你想实现自己的解析器,你可以自定义全局和每一个反射过程使用的解析器。 + +你可以继承于 `MemberProccessor.Resolver` 来实现自己的解析器。 + +> 示例如下 + +```kotlin +class MyMemberProcessorResolver : MemberProcessor.Resolver() { + + override fun getDeclaredConstructors(declaringClass: Class): List> { + // 在这里拦截并实现你的构造方法过滤逻辑 + return super.getDeclaredConstructors(declaringClass) + } + + override fun getDeclaredMethods(declaringClass: Class): List { + // 在这里拦截并实现你的方法过滤逻辑 + return super.getDeclaredMethods(declaringClass) + } + + override fun getDeclaredFields(declaringClass: Class): List { + // 在这里拦截并实现你的字段过滤逻辑 + return super.getDeclaredFields(declaringClass) + } +} +``` + +然后你可以将其设置到全局配置中。 + +> 示例如下 + +```kotlin +MemberProcessor.globalResolver = MyMemberProcessorResolver() +``` + +或者,在每次反射过程中,你可以使用 `MemberCondition.Configuration` 来设置自定义解析器,或者使用链式调用设置解析器。 + +> 示例如下 + +```kotlin +// 创建解析器 +val myResolver = MyMemberProcessorResolver() +// 假设这就是这个 Class 的实例 +val test: Test +// 使用 KavaRef 调用并执行 +test.resolve() + // 设置自定义解析器 + .processor(myResolver) + .firstMethod { + name = "doTask" + parameters(String::class) + }.invoke("task_name") +``` + +::: tip + +你可以在 [这里](../config/processor-resolvers.md) 找到一些公开维护的自定义解析器,定义在你的项目中即可使用。 + +::: + +### 关于缓存 + +由于过滤条件的多样性,KavaRef 不直接提供缓存功能,根据每个开发者的实现方式不同,缓存的实现方式也会有所不同。 + +我们建议手动对过滤结果创建的 `MemberResolver` 实现缓存以提高性能并参考 [手动创建](#手动创建) 拆分过滤条件以优化代码复用率。 + +::: danger + +如果你使用了 `val myResolver by lazy { ... }` 来实现缓存,例如下方这样做。 + +> 示例如下 + +```kotlin +val myResolver by lazy { + Test::class.resolve() + .firstMethod { + name = "doTask" + parameters(String::class) + } +} +``` + +你在调用时可能会这样做。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 使用 KavaRef 调用并执行 +myResolver.of(test).invoke("task_name") +``` + +请注意,由于 `MemberResolver` 已被缓存,在你每次引用它时调用的是同一个实例,而 `MemberResolver` 的实例对象不允许重复设置 (参考 [手动创建](#手动创建) 下方的 “特别注意”), +所以直接这样调用会抛出异常,你需要改为以下形式。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val test: Test +// 使用 KavaRef 调用并执行 +myResolver.copy().of(test).invoke("task_name") +``` + +这样一来,你就可以在每次调用时复制一个新的 `MemberResolver` 实例而不需要重复反射过程,也不会抛出异常。 + +::: + +### Java 用法 + +KavaRef 不推荐直接在 Java 中使用,因为它的 API 设计是基于 Kotlin 的特性和语法糖。 + +如果你需要在 Java 中使用 KavaRef,你可以使用以下方式来实现。 + +> 示例如下 + +```java +public class Main { + + public static void main(String[] args) { + // 假设这就是这个 Class 的实例 + Test test; + // 使用 KavaRef 调用并执行 + KavaRef.resolveClass(Test.class) + .method() + .name("doTask") + .parameters(String.class) + .build() + .get(0) + .of(test) + .invoke("task_name"); + // 或者,使用实例创建 KavaRef 反射 + KavaRef.resolveObject(test) + .method() + .name("doTask") + .parameters(String.class) + .build() + .get(0) + .invoke("task_name"); + } +} +``` \ No newline at end of file diff --git a/docs-source/src/zh-cn/library/kavaref-extension.md b/docs-source/src/zh-cn/library/kavaref-extension.md new file mode 100644 index 0000000..2e97154 --- /dev/null +++ b/docs-source/src/zh-cn/library/kavaref-extension.md @@ -0,0 +1,295 @@ +# kavaref-extension + +![Maven Central](https://img.shields.io/maven-central/v/com.highcapable.kavaref/kavaref-extension?logo=apachemaven&logoColor=orange&style=flat-square) + +![Maven metadata URL](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fraw.githubusercontent.com%2FHighCapable%2Fmaven-repository%2Frefs%2Fheads%2Fmain%2Frepository%2Freleases%2Fcom%2Fhighcapable%2Fkavaref%2Fkavaref-extension%2Fmaven-metadata.xml&logo=apachemaven&logoColor=orange&label=highcapable-maven-releases&style=flat-square) + +这是 KavaRef 相关功能的扩展依赖。 + +## 配置依赖 + +你可以使用如下方式将此模块添加到你的项目中。 + +### SweetDependency (推荐) + +在你的项目 `SweetDependency` 配置文件中添加依赖。 + +```yaml +libraries: + com.highcapable.kavaref: + kavaref-extension: + version: + +``` + +在你的项目 `build.gradle.kts` 中配置依赖。 + +```kotlin +implementation(com.highcapable.kavaref.kavaref.extension) +``` + +### Version Catalog + +在你的项目 `gradle/libs.versions.toml` 中添加依赖。 + +```toml +[versions] +kavaref-extension = "" + +[libraries] +kavaref-extension = { module = "com.highcapable.kavaref:kavaref-extension", version.ref = "kavaref-extension" } +``` + +在你的项目 `build.gradle.kts` 中配置依赖。 + +```kotlin +implementation(libs.kavaref.extension) +``` + +请将 `` 修改为此文档顶部显示的版本。 + +### 传统方式 + +在你的项目 `build.gradle.kts` 中配置依赖。 + +```kotlin +implementation("com.highcapable.kavaref:kavaref-extension:") +``` + +请将 `` 修改为此文档顶部显示的版本。 + +## 功能介绍 + +你可以 [点击这里](kdoc://kavaref-extension) 查看 KDoc。 + +### Class 扩展 + +KavaRef 提供了一些扩展,在处理 `Class` 对象时会更加方便。 + +KavaRef 对 `Class` 的扩展同样添加了 `KClass` 扩展,作用是调用 `KClass.java`,写法上比直接使用 `Some::class.java` 更加简洁。 + +#### 创建 Class 对象 + +例如我们需要使用字符串类名创建一个 `Class` 对象。 + +> 示例如下 + +```kotlin +val myClass = "com.example.MyClass".toClass() +// 你可以使用带有 OrNull 后缀的方法在找不到 Class 时返回 null 而不是抛出异常 +val myClassOrNull = "com.example.MyClass".toClassOrNull() +``` + +这些方法统一使用 `ClassLoaderProvider` 来获取默认的 `ClassLoader`,你可以设置默认的 `ClassLoader` 以影响全局功能。 + +> 示例如下 + +```kotlin +ClassLoaderProvider.classLoader = MyCustomClassLoader() +``` + +你也可以手动向 `toClass` 方法传入一个 `ClassLoader` 参数来指定使用哪个 `ClassLoader`。 + +#### Class 对象引用 + +在 Kotlin 中引用 Java Class 需要写很长的声明,例如 `MyClass::class.java`,此时你可以用以下方式来简化。 + +> 示例如下 + +```kotlin +val myClass = classOf() +``` + +你可以使用 `isSubclassOf` 方法来判断一个 `Class` 是否是另一个 `Class` 的子类。 + +> 示例如下 + +```kotlin +val isSubclass = MyClass::class isSubclassOf MySuperClass::class +// 当然,它也有一个对应的反义判断方法 +val isNotSubclass = MyClass::class isNotSubclassOf MySuperClass::class +``` + +你还可以使用 `hasSuperclass` 和 `hasInterfaces` 方法来判断一个 `Class` 是否有超类或接口。 + +::: danger + +`classOf` 方法传入的 `Class` 默认会进行 Java 包装类的拆箱操作,无论你传入的是类似 `kotlin.Boolean` 还是 `java.lang.Boolean` (参考下方的 [Java 包装类扩展](#java-包装类扩展)), +如果你需要避免传入的 `Class` 被拆箱变为原始类型,你需要明确设置 `primitiveType = false` 参数。 + +::: + +#### 创建新的实例 + +KavaRef 为 `Class` 提供了一个方法来方便地创建一个新的实例,你不需要考虑构造参数的类型,你只需要传入对应的参数即可立即创建一个新的实例。 + +> 示例如下 + +```kotlin +val myClass = MyClass::class.createInstance("Hello", 123) +// 你也可以使用带有 OrNull 后缀的方法在创建失败时返回 null 而不是抛出异常 +val myClassOrNull = MyClass::class.createInstanceOrNull("Hello", 123) +// createInstance 方法默认仅过滤公开的构造方法,如果你需要调用非公有构造方法,请设置 isPublic = false +val myClassWithPrivateConstructor = MyClass::class.createInstance("Private!", isPublic = false) +// 如果你想指定创建实例的类型使用另一个类型,可以使用以下方法 +val mySuperClass = MyClas::class.createInstanceAsType("Hello", 123) +// 同样地,你也可以使用带有 OrNull 后缀的方法在创建失败时返回 null 而不是抛出异常 +val mySuperClassOrNull = MyClass::class.createInstanceAsTypeOrNull("Hello", 123) +``` + +::: tip + +`createInstance` 方法在成功匹配一次后,会将结果进行缓存防止重复反射造成的性能损耗,它是线程安全的,你可以放心在任何标准场景下使用。 + +::: + +::: danger + +当你传入带有 `null` 的参数时,KavaRef 会尝试将其作为可匹配到条件的一部分 (模糊条件),准确性可能会下降。 + +`createInstance` 方法不允许所有参数均为 `null` 的情况 (条件完全模糊),会直接抛出异常,因为这种情况无法确定要创建哪个实例。 + +::: + +#### Class 修饰符 + +KavaRef 也对 `Modifier` 进行了扩展,你可以直接使用 `Class.isPublic` 等方法来判断一个 `Class` 的修饰符。 + +#### VariousClass + +KavaRef 提供了 `VariousClass` 类来装载不确定完整类名的 `Class` 对象,并返回成功匹配到的第一个。 + +此功能通常可用于 Android 应用中那些被 R8 混淆后的类名。 + +> 示例如下 + +```kotlin +// 假设在 A 版本中,这个类为 com.example.a, +// 在 B 版本中,这个类为 com.example.b +val myClass = VariousClass("com.example.a", "com.example.b").load() +// 你也可以使用后缀名为 OrNull 的方法在找不到 Class 时返回 null 而不是抛出异常 +val myClassOrNull = VariousClass("com.example.a", "com.example.b").loadOrNull() +``` + +#### 延迟装载 Class 对象 + +KavaRef 提供了 `LazyClass` 类来延迟装载 `Class` 对象。 + +你可以在需要时再装载 `Class`,而不是在创建时就立即装载,这可以解决一些需要运行时或运行到特定条件下才需要装载的 `Class`。 + +> 示例如下 + +```kotlin +// 定义一个不可为空延迟装载的 Class 并托管给 myClass +val myClass by lazyClass("com.example.MyClass") +// 定义一个可为空延迟装载的 Class 并托管给 myClassOrNull +val myClassOrNull by lazyClassOrNull("com.example.MyClass") +// 它亦可支持传入 VariousClass +val otherClassOrNull by lazyClassOrNull(VariousClass("com.example.a", "com.example.b")) +// 在需要时调用即装载 +myClass.resolve() +myClassOrNull?.resolve() +otherClassOrNull?.resolve() +``` + +#### ClassLoader 扩展 + +KavaRef 还为 `ClassLoader` 提供了一些实用的扩展方法。 + +> 示例如下 + +```kotlin +// 假设这就是你的 ClassLoader +val classLoader: ClassLoader +// 装载一个 Class,在装载失败时返回 null +val myClassOrNull = classLoader.loadClassOrNull("com.example.MyClass") +// 判断这个 Class 是否存在于当前 ClassLoader 中 +val isClassExists = classLoader.hasClass("com.example.MyClass") +``` + +### 数组 Class 扩展 + +在 Java 中,数组的 `Class` 对象是一个特殊的 `Class` 对象,通常,我们创建它的方式如下。 + +例如创建一个 `java.lang.String[]` 的 `Class` 对象。 + +> 示例如下 + +```kotlin +val arrayClass = java.lang.reflect.Array.newInstance(String::class.java, 0).javaClass +``` + +这样写起来非常长,而且不方便维护,所以 KavaRef 提供了一个方法来简化这个过程。 + +现在,创建 `java.lang.String[]` 的 `Class` 对象可以这样写。 + +> 示例如下 + +```kotlin +val arrayClass = ArrayClass(String::class) +``` + +### Member 扩展 + +KavaRef 提供了一些扩展方法来简化对 `Member` 的操作。 + +你可以在任何 `Member` 对象上使用 `makeAccessible` 方法来设置其可访问性。 + +如果 `Member` 是 `AccessibleObject` 类型即可生效。 + +> 示例如下 + +```kotlin +// 假设这个是你当前的 Member 对象 +val method: Method +// 设置方法可访问 +method.makeAccessible() +``` + +同样地,KavaRef 也对 `Modifier` 进行了扩展,你可以直接使用 `Member.isPublic` 等方法来判断一个 `Member` 的修饰符。 + +### Type 扩展 + +在 Java 中操作类型或泛型类型时,通常需要使用 `Type` 接口及其子接口来处理。 + +KavaRef 提供了一些扩展方法来简化对 `Type` 的操作。 + +例如,你可以将一个符合要求的 `Type` 转换为 `Class` 对象。 + +> 示例如下 + +```kotlin +val type: Type +val clazz = type.toClass() +// 你也可以使用后缀名为 OrNull 的方法在转换失败时返回 null 而不是抛出异常 +val clazzOrNull = type.toClassOrNull() +``` + +你也可以将符合要求的 `Type` 转换为 `ParameterizedType` 对象。 + +> 示例如下 + +```kotlin +val type: Type +val parameterizedType = type.asParameterizedType() +// 你也可以使用后缀名为 OrNull 的方法在转换失败时返回 null 而不是抛出异常 +val parameterizedTypeOrNull = type.asParameterizedTypeOrNull() +``` + +你还可以使用以下方式获取超类中的泛型参数数组,这在一些超类与子类的封装操作中会经常用到。 + +> 示例如下 + +```kotlin +val myClass: Class<*> +// 获取 myClass 的超类的泛型参数数组,获取失败或无法获取时将返回空数组 +val arguments = myClass.genericSuperclassTypeArguments() +``` + +### Java 包装类扩展 + +在 Kotlin 中直接使用 `Boolean::class`、`Byte::class` 等方式获取到的是 Java 的原始类型 `boolean`、`byte` 而不是它们的包装类。 + +如果你需要获取 Java 的包装类,你需要使用完整的 `java.lang.Boolean::class`、`java.lang.Byte::class` 等方式或使用 `Boolean::class.javaObjectType`、`Byte::class.javaObjectType`。 + +所以,KavaRef 提供了一些类型别名来处理 Java 的包装类,现在你只需要在这些类型加上 `J` 前缀即可,例如 `JBoolean::class`, +它等价于 `java.lang.Boolean::class`,部分类型需要填写全称,例如 `JInteger::class`。 \ No newline at end of file diff --git a/docs-source/yarn.lock b/docs-source/yarn.lock new file mode 100644 index 0000000..4f88786 --- /dev/null +++ b/docs-source/yarn.lock @@ -0,0 +1,2004 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/parser@^7.18.4": + version "7.18.13" + resolved "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.18.13.tgz#5b2dd21cae4a2c5145f1fbd8ca103f9313d3b7e4" + integrity sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg== + +"@babel/parser@^7.23.5": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" + integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== + +"@esbuild/android-arm64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.9.tgz#683794bdc3d27222d3eced7b74cad15979548031" + integrity sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ== + +"@esbuild/android-arm@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.9.tgz#21a4de41f07b2af47401c601d64dfdefd056c595" + integrity sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA== + +"@esbuild/android-x64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.9.tgz#e2d7674bc025ddc8699f0cc76cb97823bb63c252" + integrity sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA== + +"@esbuild/darwin-arm64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.9.tgz#ae7a582289cc5c0bac15d4b9020a90cb7288f1e9" + integrity sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw== + +"@esbuild/darwin-x64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.9.tgz#8a216c66dcf51addeeb843d8cfaeff712821d12b" + integrity sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ== + +"@esbuild/freebsd-arm64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.9.tgz#63d4f603e421252c3cd836b18d01545be7c6c440" + integrity sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g== + +"@esbuild/freebsd-x64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.9.tgz#a3db52595be65360eae4de1d1fa3c1afd942e1e4" + integrity sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA== + +"@esbuild/linux-arm64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.9.tgz#4ae5811ce9f8d7df5eb9edd9765ea9401a534f13" + integrity sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ== + +"@esbuild/linux-arm@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.9.tgz#9807e92cfd335f46326394805ad488e646e506f2" + integrity sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw== + +"@esbuild/linux-ia32@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.9.tgz#18892c10f3106652b16f9da88a0362dc95ed46c7" + integrity sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q== + +"@esbuild/linux-loong64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.9.tgz#dc2ebf9a125db0a1bba18c2bbfd4fbdcbcaf61c2" + integrity sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA== + +"@esbuild/linux-mips64el@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.9.tgz#4c2f7c5d901015e3faf1563c4a89a50776cb07fd" + integrity sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw== + +"@esbuild/linux-ppc64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.9.tgz#8385332713b4e7812869622163784a5633f76fc4" + integrity sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ== + +"@esbuild/linux-riscv64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.9.tgz#23f1db24fa761be311874f32036c06249aa20cba" + integrity sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg== + +"@esbuild/linux-s390x@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.9.tgz#2dffe497726b897c9f0109e774006e25b33b4fd0" + integrity sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw== + +"@esbuild/linux-x64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.9.tgz#ceb1d62cd830724ff5b218e5d3172a8bad59420e" + integrity sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A== + +"@esbuild/netbsd-x64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.9.tgz#0cbca65e9ef4d3fc41502d3e055e6f49479a8f18" + integrity sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug== + +"@esbuild/openbsd-x64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.9.tgz#1f57adfbee09c743292c6758a3642e875bcad1cf" + integrity sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw== + +"@esbuild/sunos-x64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.9.tgz#116be6adbd2c7479edeeb5f6ea0441002ab4cb9c" + integrity sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw== + +"@esbuild/win32-arm64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.9.tgz#2be22131ab18af4693fd737b161d1ef34de8ca9d" + integrity sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg== + +"@esbuild/win32-ia32@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.9.tgz#e10ead5a55789b167b4225d2469324538768af7c" + integrity sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg== + +"@esbuild/win32-x64@0.19.9": + version "0.19.9" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.9.tgz#b2da6219b603e3fa371a78f53f5361260d0c5585" + integrity sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ== + +"@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@mdit-vue/plugin-component@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-component/-/plugin-component-1.0.0.tgz#fdc41e58ef8b2207c94864657b27bb52f891bea9" + integrity sha512-ZXsJwxkG5yyTHARIYbR74cT4AZ0SfMokFFjiHYCbypHIeYWgJhso4+CZ8+3V9EWFG3EHlGoKNGqKp9chHnqntQ== + dependencies: + "@types/markdown-it" "^13.0.1" + markdown-it "^13.0.1" + +"@mdit-vue/plugin-frontmatter@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-frontmatter/-/plugin-frontmatter-1.0.0.tgz#c968335a96c0c65e623ba3e4cc6fb89a8e5a012b" + integrity sha512-MMA7Ny+YPZA7eDOY1t4E+rKuEWO39mzDdP/M68fKdXJU6VfcGkPr7gnpnJfW2QBJ5qIvMrK/3lDAA2JBy5TfpA== + dependencies: + "@mdit-vue/types" "1.0.0" + "@types/markdown-it" "^13.0.1" + gray-matter "^4.0.3" + markdown-it "^13.0.1" + +"@mdit-vue/plugin-headers@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-headers/-/plugin-headers-1.0.0.tgz#4e3d9f13d69ec2de325a5502ba08da09f62f2cd6" + integrity sha512-0rK/iKy6x13d/Pp5XxdLBshTD0+YjZvtHIaIV+JO+/H2WnOv7oaRgs48G5d44z3XJVUE2u6fNnTlI169fef0/A== + dependencies: + "@mdit-vue/shared" "1.0.0" + "@mdit-vue/types" "1.0.0" + "@types/markdown-it" "^13.0.1" + markdown-it "^13.0.1" + +"@mdit-vue/plugin-sfc@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-sfc/-/plugin-sfc-1.0.0.tgz#ecebfe3483db009a03bca9b9cebf549c0b31591c" + integrity sha512-agMUe0fY4YHxsZivSvplBwRwrFvsIf/JNUJCAYq1+2Sg9+2hviTBZwjZDxYqHDHOVLtiNr+wuo68tE24mAx3AQ== + dependencies: + "@mdit-vue/types" "1.0.0" + "@types/markdown-it" "^13.0.1" + markdown-it "^13.0.1" + +"@mdit-vue/plugin-title@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-title/-/plugin-title-1.0.0.tgz#6010990870d24bc86577b88890c92fcf3344e566" + integrity sha512-8yC60fCZ95xcJ/cvJH4Lv43Rs4k+33UGyKrRWj5J8TNyMwUyGcwur0XyPM+ffJH4/Bzq4myZLsj/TTFSkXRxvw== + dependencies: + "@mdit-vue/shared" "1.0.0" + "@mdit-vue/types" "1.0.0" + "@types/markdown-it" "^13.0.1" + markdown-it "^13.0.1" + +"@mdit-vue/plugin-toc@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-toc/-/plugin-toc-1.0.0.tgz#d961b537643b3dc1c9a17f2739f1361b9839f031" + integrity sha512-WN8blfX0X/5Nolic0ClDWP7eVo9IB+U4g0jbycX3lolIZX5Bai1UpsD3QYZr5VVsPbQJMKMGvTrCEtCNTGvyWQ== + dependencies: + "@mdit-vue/shared" "1.0.0" + "@mdit-vue/types" "1.0.0" + "@types/markdown-it" "^13.0.1" + markdown-it "^13.0.1" + +"@mdit-vue/shared@1.0.0", "@mdit-vue/shared@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mdit-vue/shared/-/shared-1.0.0.tgz#d8456da39c08c20ccadef7ba7321ecff4b05d330" + integrity sha512-nbYBfmEi+pR2Lm0Z6TMVX2/iBjfr/kGEsHW8CC0rQw+3+sG5dY6VG094HuFAkiAmmvZx9DZZb+7ZMWp9vkwCRw== + dependencies: + "@mdit-vue/types" "1.0.0" + "@types/markdown-it" "^13.0.1" + markdown-it "^13.0.1" + +"@mdit-vue/types@1.0.0", "@mdit-vue/types@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mdit-vue/types/-/types-1.0.0.tgz#4950ae987a7c0d8377122bd3b09a53536911ca38" + integrity sha512-xeF5+sHLzRNF7plbksywKCph4qli20l72of2fMlZQQ7RECvXYrRkE9+bjRFQCyULC7B8ydUYbpbkux5xJlVWyw== + +"@mr-hope/vuepress-plugin-copy-code@^1.30.0": + version "1.30.0" + resolved "https://repo.huaweicloud.com/repository/npm/@mr-hope/vuepress-plugin-copy-code/-/vuepress-plugin-copy-code-1.30.0.tgz#ab2ad1fb418948c1513ab5d7daeb343547f07b69" + integrity sha512-odDox9FKRjfW9RHNo30TlmAe74VVMmn4vjqfJ/nfzBkgghZArz7CdzIfg8zSfJ4PaQoNxEc57uAQ7w/jeM2Efg== + dependencies: + "@mr-hope/vuepress-shared" "1.30.0" + "@mr-hope/vuepress-types" "1.30.0" + balloon-css "^1.2.0" + +"@mr-hope/vuepress-shared@1.30.0": + version "1.30.0" + resolved "https://repo.huaweicloud.com/repository/npm/@mr-hope/vuepress-shared/-/vuepress-shared-1.30.0.tgz#c93949160b86cb03fb57c0a6e91a8d75bf48081d" + integrity sha512-yJS0IaWJ8eCJXKCpY0TjFU5xhgrIg11SoFQjq0KuR8C7VMYsC9JPPv/tV5g0d9SAdshooUinibO3rOZcwHbMHA== + dependencies: + "@mr-hope/vuepress-types" "1.30.0" + +"@mr-hope/vuepress-types@1.30.0": + version "1.30.0" + resolved "https://repo.huaweicloud.com/repository/npm/@mr-hope/vuepress-types/-/vuepress-types-1.30.0.tgz#938d45213ba7099e817ef6df46abf2d5ce437b2d" + integrity sha512-XsPxmevdUxrKKx5l/1JS/wkzzRWMcd1wGrQaahETUbeZ9HuxUCNrLnUc9aH8ngss91LZ01qPiAx69zxIBQUcOw== + dependencies: + "@types/express" "^4.17.13" + "@types/markdown-it" "^10.0.3" + "@types/markdown-it-anchor" "^7.0.0" + "@types/webpack-dev-server" "^3.11.6" + cac "^6.7.12" + vue "^2.6.14" + webpack-chain "^6.5.1" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://repo.huaweicloud.com/repository/npm/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://repo.huaweicloud.com/repository/npm/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://repo.huaweicloud.com/repository/npm/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@rollup/rollup-android-arm-eabi@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.1.tgz#beaf518ee45a196448e294ad3f823d2d4576cf35" + integrity sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig== + +"@rollup/rollup-android-arm64@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.1.tgz#6f76cfa759c2d0fdb92122ffe28217181a1664eb" + integrity sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ== + +"@rollup/rollup-darwin-arm64@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.1.tgz#9aaefe33a5481d66322d1c62f368171c03eabe2b" + integrity sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA== + +"@rollup/rollup-darwin-x64@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.1.tgz#707dcaadcdc6bd3fd6c69f55d9456cd4446306a3" + integrity sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og== + +"@rollup/rollup-linux-arm-gnueabihf@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.1.tgz#7a4dbbd1dd98731d88a55aefcef0ec4c578fa9c7" + integrity sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q== + +"@rollup/rollup-linux-arm64-gnu@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.1.tgz#967ba8e6f68a5f21bd00cd97773dcdd6107e94ed" + integrity sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q== + +"@rollup/rollup-linux-arm64-musl@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.1.tgz#d3a4e1c9f21eef3b9f4e4989f334a519a1341462" + integrity sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw== + +"@rollup/rollup-linux-riscv64-gnu@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.1.tgz#415c0533bb752164effd05f5613858e8f6779bc9" + integrity sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw== + +"@rollup/rollup-linux-x64-gnu@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.1.tgz#0983385dd753a2e0ecaddea7a81dd37fea5114f5" + integrity sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg== + +"@rollup/rollup-linux-x64-musl@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.1.tgz#eb7494ebc5199cbd2e5c38c2b8acbe2603f35e03" + integrity sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw== + +"@rollup/rollup-win32-arm64-msvc@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.1.tgz#5bebc66e3a7f82d4b9aa9ff448e7fc13a69656e9" + integrity sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g== + +"@rollup/rollup-win32-ia32-msvc@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.1.tgz#34156ebf8b4de3b20e6497260fe519a30263f8cf" + integrity sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg== + +"@rollup/rollup-win32-x64-msvc@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.1.tgz#d146db7a5949e10837b323ce933ed882ac878262" + integrity sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA== + +"@sindresorhus/merge-streams@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz#9cd84cc15bc865a5ca35fcaae198eb899f7b5c90" + integrity sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw== + +"@types/body-parser@*": + version "1.19.2" + resolved "https://repo.huaweicloud.com/repository/npm/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect-history-api-fallback@*": + version "1.3.5" + resolved "https://repo.huaweicloud.com/repository/npm/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" + integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://repo.huaweicloud.com/repository/npm/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/debug@^4.1.12": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": + version "4.17.30" + resolved "https://repo.huaweicloud.com/repository/npm/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz#0f2f99617fa8f9696170c46152ccf7500b34ac04" + integrity sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.13" + resolved "https://repo.huaweicloud.com/repository/npm/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/fs-extra@^11.0.4": + version "11.0.4" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" + integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== + dependencies: + "@types/jsonfile" "*" + "@types/node" "*" + +"@types/hash-sum@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/hash-sum/-/hash-sum-1.0.2.tgz#32e6e4343ee25914b2a3822f27e8e641ca534f63" + integrity sha512-UP28RddqY8xcU0SCEp9YKutQICXpaAq9N8U2klqF5hegGha7KzTOL8EdhIIV3bOSGBzjEpN9bU/d+nNZBdJYVw== + +"@types/highlight.js@^9.7.0": + version "9.12.4" + resolved "https://repo.huaweicloud.com/repository/npm/@types/highlight.js/-/highlight.js-9.12.4.tgz#8c3496bd1b50cc04aeefd691140aa571d4dbfa34" + integrity sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww== + +"@types/http-proxy@^1.17.5": + version "1.17.9" + resolved "https://repo.huaweicloud.com/repository/npm/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a" + integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw== + dependencies: + "@types/node" "*" + +"@types/jsonfile@*": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.2.tgz#d3b8a3536c5bb272ebee0f784180e456b7691c8f" + integrity sha512-8t92P+oeW4d/CRQfJaSqEwXujrhH4OEeHRjGU3v1Q8mUS8GPF3yiX26sw4svv6faL2HfBtGTe2xWIoVgN3dy9w== + dependencies: + "@types/node" "*" + +"@types/linkify-it@*": + version "3.0.2" + resolved "https://repo.huaweicloud.com/repository/npm/@types/linkify-it/-/linkify-it-3.0.2.tgz#fd2cd2edbaa7eaac7e7f3c1748b52a19143846c9" + integrity sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA== + +"@types/markdown-it-anchor@^7.0.0": + version "7.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/@types/markdown-it-anchor/-/markdown-it-anchor-7.0.0.tgz#4de1bea40ebf7643990ef35c51908e037bfa48a8" + integrity sha512-kFehISVfgSePSp6JWGWrcgKIPoPpAsZAqTLUt0Wq9+MNiGdnWdXQy8UerwhmD2afmjux1TWf8XCmlpL2/6ax/A== + dependencies: + markdown-it-anchor "*" + +"@types/markdown-it-emoji@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/markdown-it-emoji/-/markdown-it-emoji-2.0.4.tgz#5bb4293eb8699962710b3b67f10fa211730c28c3" + integrity sha512-H6ulk/ZmbDxOayPwI/leJzrmoW1YKX1Z+MVSCHXuYhvqckV4I/c+hPTf6UiqJyn2avWugfj30XroheEb6/Ekqg== + dependencies: + "@types/markdown-it" "*" + +"@types/markdown-it@*": + version "12.2.3" + resolved "https://repo.huaweicloud.com/repository/npm/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51" + integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ== + dependencies: + "@types/linkify-it" "*" + "@types/mdurl" "*" + +"@types/markdown-it@^10.0.3": + version "10.0.3" + resolved "https://repo.huaweicloud.com/repository/npm/@types/markdown-it/-/markdown-it-10.0.3.tgz#a9800d14b112c17f1de76ec33eff864a4815eec7" + integrity sha512-daHJk22isOUvNssVGF2zDnnSyxHhFYhtjeX4oQaKD6QzL3ZR1QSgiD1g+Q6/WSWYVogNXYDXODtbgW/WiFCtyw== + dependencies: + "@types/highlight.js" "^9.7.0" + "@types/linkify-it" "*" + "@types/mdurl" "*" + highlight.js "^9.7.0" + +"@types/markdown-it@^13.0.1": + version "13.0.1" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-13.0.1.tgz#2f45bd54315f5db2d9bd909ec7fd4d994cf75df4" + integrity sha512-SUEb8Frsxs3D5Gg9xek6i6EG6XQ5s+O+ZdQzIPESZVZw3Pv3CPQfjCJBI+RgqZd1IBeu18S0Rn600qpPnEK37w== + dependencies: + "@types/linkify-it" "*" + "@types/mdurl" "*" + +"@types/markdown-it@^13.0.6": + version "13.0.7" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-13.0.7.tgz#4a495115f470075bd4434a0438ac477a49c2e152" + integrity sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA== + dependencies: + "@types/linkify-it" "*" + "@types/mdurl" "*" + +"@types/mdurl@*": + version "1.0.2" + resolved "https://repo.huaweicloud.com/repository/npm/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" + integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== + +"@types/mime@*": + version "3.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/ms@*": + version "0.7.31" + resolved "https://repo.huaweicloud.com/repository/npm/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" + integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== + +"@types/node@*": + version "18.7.13" + resolved "https://repo.huaweicloud.com/repository/npm/@types/node/-/node-18.7.13.tgz#23e6c5168333480d454243378b69e861ab5c011a" + integrity sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw== + +"@types/qs@*": + version "6.9.7" + resolved "https://repo.huaweicloud.com/repository/npm/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://repo.huaweicloud.com/repository/npm/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/serve-static@*": + version "1.15.0" + resolved "https://repo.huaweicloud.com/repository/npm/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" + integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== + dependencies: + "@types/mime" "*" + "@types/node" "*" + +"@types/source-list-map@*": + version "0.1.2" + resolved "https://repo.huaweicloud.com/repository/npm/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" + integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== + +"@types/tapable@^1": + version "1.0.8" + resolved "https://repo.huaweicloud.com/repository/npm/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" + integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== + +"@types/uglify-js@*": + version "3.17.0" + resolved "https://repo.huaweicloud.com/repository/npm/@types/uglify-js/-/uglify-js-3.17.0.tgz#95271e7abe0bf7094c60284f76ee43232aef43b9" + integrity sha512-3HO6rm0y+/cqvOyA8xcYLweF0TKXlAxmQASjbOi49Co51A1N4nR4bEwBgRoD9kNM+rqFGArjKr654SLp2CoGmQ== + dependencies: + source-map "^0.6.1" + +"@types/web-bluetooth@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597" + integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow== + +"@types/webpack-dev-server@^3.11.6": + version "3.11.6" + resolved "https://repo.huaweicloud.com/repository/npm/@types/webpack-dev-server/-/webpack-dev-server-3.11.6.tgz#d8888cfd2f0630203e13d3ed7833a4d11b8a34dc" + integrity sha512-XCph0RiiqFGetukCTC3KVnY1jwLcZ84illFRMbyFzCcWl90B/76ew0tSqF46oBhnLC4obNDG7dMO0JfTN0MgMQ== + dependencies: + "@types/connect-history-api-fallback" "*" + "@types/express" "*" + "@types/serve-static" "*" + "@types/webpack" "^4" + http-proxy-middleware "^1.0.0" + +"@types/webpack-sources@*": + version "3.2.0" + resolved "https://repo.huaweicloud.com/repository/npm/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b" + integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg== + dependencies: + "@types/node" "*" + "@types/source-list-map" "*" + source-map "^0.7.3" + +"@types/webpack@^4": + version "4.41.32" + resolved "https://repo.huaweicloud.com/repository/npm/@types/webpack/-/webpack-4.41.32.tgz#a7bab03b72904070162b2f169415492209e94212" + integrity sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg== + dependencies: + "@types/node" "*" + "@types/tapable" "^1" + "@types/uglify-js" "*" + "@types/webpack-sources" "*" + anymatch "^3.0.0" + source-map "^0.6.0" + +"@vitejs/plugin-vue@^4.5.0": + version "4.5.2" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-4.5.2.tgz#1212d81bc83680e14448fefe55abd9fe1ed49ed1" + integrity sha512-UGR3DlzLi/SaVBPX0cnSyE37vqxU3O6chn8l0HJNzQzDia6/Au2A4xKv+iIJW8w2daf80G7TYHhi1pAUjdZ0bQ== + +"@vue/compiler-core@3.3.12": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.3.12.tgz#3346c0f55ce0d59e17c21d9eef9154b70c19931b" + integrity sha512-qAtjyG3GBLG0chzp5xGCyRLLe6wFCHmjI82aGzwuGKyznNP+GJJMxjc0wOYWDB2YKfho7niJFdoFpo0CZZQg9w== + dependencies: + "@babel/parser" "^7.23.5" + "@vue/shared" "3.3.12" + estree-walker "^2.0.2" + source-map-js "^1.0.2" + +"@vue/compiler-dom@3.3.12": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.3.12.tgz#267c54b388d58f30fc120ea496ebf27d4ea8368b" + integrity sha512-RdJU9oEYaoPKUdGXCy0l+i4clesdDeLmbvRlszoc9iagsnBnMmQtYfCPVQ5BHB6o7K4SCucDdJM2Dh3oXB0D6g== + dependencies: + "@vue/compiler-core" "3.3.12" + "@vue/shared" "3.3.12" + +"@vue/compiler-sfc@2.7.10": + version "2.7.10" + resolved "https://repo.huaweicloud.com/repository/npm/@vue/compiler-sfc/-/compiler-sfc-2.7.10.tgz#3fe08e780053a3bbf41328c65ae5dfdee0385206" + integrity sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q== + dependencies: + "@babel/parser" "^7.18.4" + postcss "^8.4.14" + source-map "^0.6.1" + +"@vue/compiler-sfc@3.3.12": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.3.12.tgz#6ec2c19858f264671457699c1f3a0a6fedf429fe" + integrity sha512-yy5b9e7b79dsGbMmglCe/YnhCQgBkHO7Uf6JfjWPSf2/5XH+MKn18LhzhHyxbHdJgnA4lZCqtXzLaJz8Pd8lMw== + dependencies: + "@babel/parser" "^7.23.5" + "@vue/compiler-core" "3.3.12" + "@vue/compiler-dom" "3.3.12" + "@vue/compiler-ssr" "3.3.12" + "@vue/reactivity-transform" "3.3.12" + "@vue/shared" "3.3.12" + estree-walker "^2.0.2" + magic-string "^0.30.5" + postcss "^8.4.32" + source-map-js "^1.0.2" + +"@vue/compiler-ssr@3.3.12": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.3.12.tgz#e62499c6003ccd09acb7190167d08845e3a0eaa5" + integrity sha512-adCiMJPznfWcQyk/9HSuXGja859IaMV+b8UNSVzDatqv7h0PvT9BEeS22+gjkWofDiSg5d78/ZLls3sLA+cn3A== + dependencies: + "@vue/compiler-dom" "3.3.12" + "@vue/shared" "3.3.12" + +"@vue/devtools-api@^6.5.0": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07" + integrity sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q== + +"@vue/devtools-api@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz#7f71f31e40973eeee65b9a64382b13593fdbd697" + integrity sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA== + +"@vue/reactivity-transform@3.3.12": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.3.12.tgz#4cb871b597eb8b321577b4d7f1e93eaebca16128" + integrity sha512-g5TijmML7FyKkLt6QnpqNmA4KD7K/T5SbXa88Bhq+hydNQEkzA8veVXWAQuNqg9rjaFYD0rPf0a9NofKA0ENgg== + dependencies: + "@babel/parser" "^7.23.5" + "@vue/compiler-core" "3.3.12" + "@vue/shared" "3.3.12" + estree-walker "^2.0.2" + magic-string "^0.30.5" + +"@vue/reactivity@3.3.12": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.3.12.tgz#b4a62a7678ab20c1ef32f991342ddbb8532417da" + integrity sha512-vOJORzO8DlIx88cgTnMLIf2GlLYpoXAKsuoQsK6SGdaqODjxO129pVPTd2s/N/Mb6KKZEFIHIEwWGmtN4YPs+g== + dependencies: + "@vue/shared" "3.3.12" + +"@vue/runtime-core@3.3.12": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.3.12.tgz#67ee6cfc2e85d656946975239ea635ec42dde5f6" + integrity sha512-5iL4w7MZrSGKEZU2wFAYhDZdZmgn+s//73EfgDXW1M+ZUOl36md7tlWp1QFK/ladiq4FvQ82shVjo0KiPDPr0A== + dependencies: + "@vue/reactivity" "3.3.12" + "@vue/shared" "3.3.12" + +"@vue/runtime-dom@3.3.12": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.3.12.tgz#28a239496e589037774cba7c1b27057242eedb11" + integrity sha512-8mMzqiIdl+IYa/OXwKwk6/4ebLq7cYV1pUcwCSwBK2KerUa6cwGosen5xrCL9f8o2DJ9TfPFwbPEvH7OXzUpoA== + dependencies: + "@vue/runtime-core" "3.3.12" + "@vue/shared" "3.3.12" + csstype "^3.1.3" + +"@vue/server-renderer@3.3.12": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.3.12.tgz#f0246aba5d5d6fdfa840ac9e4f32d76f03b20665" + integrity sha512-OZ0IEK5TU5GXb5J8/wSplyxvGGdIcwEmS8EIO302Vz8K6fGSgSJTU54X0Sb6PaefzZdiN3vHsLXO8XIeF8crQQ== + dependencies: + "@vue/compiler-ssr" "3.3.12" + "@vue/shared" "3.3.12" + +"@vue/shared@3.3.12", "@vue/shared@^3.3.8": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.12.tgz#7c030c4e2f1db8beb638b159cbb86d0ff78c3198" + integrity sha512-6p0Yin0pclvnER7BLNOQuod9Z+cxSYh8pSh7CzHnWNjAIP6zrTlCdHRvSCb1aYEx6i3Q3kvfuWU7nG16CgG1ag== + +"@vuepress/bundler-vite@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/bundler-vite/-/bundler-vite-2.0.0-rc.0.tgz#22551deb85c5fefe434c02685ef3c037e06d513f" + integrity sha512-rX8S8IYpqqlJfNPstS/joorpxXx/4WuE7+gDM31i2HUrxOKGZVzq8ZsRRRU2UdoTwHZSd3LpUS4sMtxE5xLK1A== + dependencies: + "@vitejs/plugin-vue" "^4.5.0" + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + autoprefixer "^10.4.16" + connect-history-api-fallback "^2.0.0" + postcss "^8.4.31" + postcss-load-config "^4.0.1" + rollup "^4.4.1" + vite "~5.0.0" + vue "^3.3.8" + vue-router "^4.2.5" + +"@vuepress/cli@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/cli/-/cli-2.0.0-rc.0.tgz#387b423121560247192dcdf27a391fa425adbbaf" + integrity sha512-XWSIFO9iOR7N4O2lXIwS5vZuLjU9WU/aGAtmhMWEMxrdMx7TQaJbgrfpTUEbHMf+cPI1DXBbUbtmkqIvtfOV0w== + dependencies: + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + cac "^6.7.14" + chokidar "^3.5.3" + envinfo "^7.11.0" + esbuild "~0.19.5" + +"@vuepress/client@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/client/-/client-2.0.0-rc.0.tgz#851677f81d90ad4fb5d5fd4c5693c499b3dd0ad2" + integrity sha512-TwQx8hJgYONYxX+QltZ2aw9O5Ym6SKelfiUduuIRb555B1gece/jSVap3H/ZwyBhpgJMtG4+/Mrmf8nlDSHjvw== + dependencies: + "@vue/devtools-api" "^6.5.1" + "@vuepress/shared" "2.0.0-rc.0" + "@vueuse/core" "^10.6.1" + vue "^3.3.8" + vue-router "^4.2.5" + +"@vuepress/core@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/core/-/core-2.0.0-rc.0.tgz#b4a8f68a58e755ecc23d330c7d7769270ce792e4" + integrity sha512-uoOaZP1MdxZYJIAJcRcmYKKeCIVnxZeOuLMOOB9CPuAKSalT1RvJ1lztw6RX3q9SPnlqtSZPQXDncPAZivw4pA== + dependencies: + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/markdown" "2.0.0-rc.0" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + vue "^3.3.8" + +"@vuepress/markdown@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/markdown/-/markdown-2.0.0-rc.0.tgz#513de09b470306e3a647ce2901995d0e63efbc0e" + integrity sha512-USmqdKKMT6ZFHYRztTjKUlO8qgGfnEygMAAq4AzC/uYXiEfrbMBLAWJhteyGS56P3rGLj0OPAhksE681bX/wOg== + dependencies: + "@mdit-vue/plugin-component" "^1.0.0" + "@mdit-vue/plugin-frontmatter" "^1.0.0" + "@mdit-vue/plugin-headers" "^1.0.0" + "@mdit-vue/plugin-sfc" "^1.0.0" + "@mdit-vue/plugin-title" "^1.0.0" + "@mdit-vue/plugin-toc" "^1.0.0" + "@mdit-vue/shared" "^1.0.0" + "@mdit-vue/types" "^1.0.0" + "@types/markdown-it" "^13.0.6" + "@types/markdown-it-emoji" "^2.0.4" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + markdown-it "^13.0.2" + markdown-it-anchor "^8.6.7" + markdown-it-emoji "^2.0.2" + mdurl "^1.0.1" + +"@vuepress/plugin-active-header-links@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-active-header-links/-/plugin-active-header-links-2.0.0-rc.0.tgz#bd542ba9964de36c8650b29f7fd238d28cedfb64" + integrity sha512-UJdXLYNGL5Wjy5YGY8M2QgqT75bZ95EHebbqGi8twBdIJE9O+bM+dPJyYtAk2PIVqFORiw3Hj+PchsNSxdn9+g== + dependencies: + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + ts-debounce "^4.0.0" + vue "^3.3.8" + vue-router "^4.2.5" + +"@vuepress/plugin-back-to-top@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-back-to-top/-/plugin-back-to-top-2.0.0-rc.0.tgz#686b00890ed5feae28800cdb417edce9a9070181" + integrity sha512-6GPfuzV5lkAnR00BxRUhqMXwMWt741alkq2R6bln4N8BneSOwEpX/7vi19MGf232aKdS/Va4pF5p0/nJ8Sed/g== + dependencies: + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + ts-debounce "^4.0.0" + vue "^3.3.8" + +"@vuepress/plugin-container@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-container/-/plugin-container-2.0.0-rc.0.tgz#27153a5c3b0cd8818337aa85a1a5c98bde49f63e" + integrity sha512-b7vrLN11YE7qiUDPfA3N9P7Z8fupe9Wbcr9KAE/bmfZ9VT4d6kzpVyoU7XHi99XngitsmnkaXP4aBvBF1c2AnA== + dependencies: + "@types/markdown-it" "^13.0.6" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/markdown" "2.0.0-rc.0" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + markdown-it "^13.0.2" + markdown-it-container "^3.0.0" + +"@vuepress/plugin-external-link-icon@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-external-link-icon/-/plugin-external-link-icon-2.0.0-rc.0.tgz#d1bb78cedb99370ea4dad7522f7b234e50243eb8" + integrity sha512-o8bk0oIlj/BkKc02mq91XLDloq1VOz/8iNcRwKAeqBE6svXzdYiyoTGet0J/4iPuAetsCn75S57W6RioDJHMnQ== + dependencies: + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/markdown" "2.0.0-rc.0" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + vue "^3.3.8" + +"@vuepress/plugin-git@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-git/-/plugin-git-2.0.0-rc.0.tgz#fb62cfbaf13d5f06310ae50026cd43f10a32f5b1" + integrity sha512-r7UF77vZxaYeJQLygzodKv+15z3/dTLuGp4VcYO21W6BlJZvd4u9zqgiV7A//bZQvK4+3Hprylr0G3KgXqMewA== + dependencies: + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + execa "^8.0.1" + +"@vuepress/plugin-medium-zoom@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-medium-zoom/-/plugin-medium-zoom-2.0.0-rc.0.tgz#9e9006986aaf4d343a6fdfe8941b3182f8931b85" + integrity sha512-peU1lYKsmKikIe/0pkJuHzD/k6xW2TuqdvKVhV4I//aOE1WxsREKJ4ACcldmoIsnysoDydAUqKT6xDPGyDsH2g== + dependencies: + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + medium-zoom "^1.1.0" + vue "^3.3.8" + +"@vuepress/plugin-nprogress@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-nprogress/-/plugin-nprogress-2.0.0-rc.0.tgz#ba5b932db095c9a58a6b1b64c73f21332bd173b6" + integrity sha512-rI+eK0Pg1KiZE+7hGmDUeSbgdWCid8Vnw0hFKNmjinDzGVmx4m03M6qfvclsI0SryH+lR7itZGLaR4gbTlrz/w== + dependencies: + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + vue "^3.3.8" + vue-router "^4.2.5" + +"@vuepress/plugin-palette@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-palette/-/plugin-palette-2.0.0-rc.0.tgz#57445772fda0556ca236fcde9c1e325b0ab17772" + integrity sha512-wW70SCp3/K7s1lln5YQsBGTog2WXaQv5piva5zhXcQ47YGf4aAJpThDa5C/ot4HhkPOKn8Iz5s0ckxXZzW8DIg== + dependencies: + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + chokidar "^3.5.3" + +"@vuepress/plugin-prismjs@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-prismjs/-/plugin-prismjs-2.0.0-rc.0.tgz#c2eb8b9aaf2dd955965ab6684dd2fb638619c114" + integrity sha512-c5WRI7+FhVjdbymOKQ8F2KY/Bnv7aQtWScVk8vCMUimNi7v7Wff/A/i3KSFNz/tge3LxiAeH/Dc2WS/OnQXwCg== + dependencies: + "@vuepress/core" "2.0.0-rc.0" + prismjs "^1.29.0" + +"@vuepress/plugin-search@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-search/-/plugin-search-2.0.0-rc.0.tgz#7ebedf697290779f51e421633fd0faf89736f298" + integrity sha512-1ikJUgIN+7QrcAftxpWUKTrNVHEN2+k/az0Sjz7Ok7EthMHcG6qQsIb+AoK4WIQMsJkwVPLxwym/M1FbBTZDWQ== + dependencies: + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + chokidar "^3.5.3" + vue "^3.3.8" + vue-router "^4.2.5" + +"@vuepress/plugin-shiki@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-shiki/-/plugin-shiki-2.0.0-rc.0.tgz#523f2673730948e262c5744d29c2d2458274ae56" + integrity sha512-K06icizhp0zVUtWa6rqL/SKWzzSP+XgYizRoqwdMsGlYNThLXAf4cIseRjF+I4VOFS5aj5hZs8MnxymKmRrwIQ== + dependencies: + "@vuepress/core" "2.0.0-rc.0" + shiki "^0.14.5" + +"@vuepress/plugin-theme-data@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-theme-data/-/plugin-theme-data-2.0.0-rc.0.tgz#ec5f7416fd79cb523277ff41e96408246ee5c004" + integrity sha512-FXY3/Ml+rM6gNKvwdBF6vKAcwnSvtXCzKgQwJAw3ppQTKUkLcbOxqM+h4d8bzHWAAvdnEvQFug5uEZgWllBQbA== + dependencies: + "@vue/devtools-api" "^6.5.1" + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + vue "^3.3.8" + +"@vuepress/shared@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/shared/-/shared-2.0.0-rc.0.tgz#0d6ef42940dd030d575c877160fb0583791a9d6c" + integrity sha512-ikdSfjRv5LGM1iv4HHwF9P6gqTjaFCXKPK+hzlkHFHNZO1GLqk7/BPc4F51tAG1s8TcLhUZc+54LrfgS7PkXXA== + dependencies: + "@mdit-vue/types" "^1.0.0" + "@vue/shared" "^3.3.8" + +"@vuepress/theme-default@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/theme-default/-/theme-default-2.0.0-rc.0.tgz#27c1bf7e9d7166e67e5a602616784e4f7e2ad1f5" + integrity sha512-I8Y08evDmMuD1jh3NftPpFFSlCWOizQDJLjN7EQwcg7jiAP4A7c2REo6nBN2EmP24Mi7UrRM+RnytHR5V+pElA== + dependencies: + "@vuepress/client" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/plugin-active-header-links" "2.0.0-rc.0" + "@vuepress/plugin-back-to-top" "2.0.0-rc.0" + "@vuepress/plugin-container" "2.0.0-rc.0" + "@vuepress/plugin-external-link-icon" "2.0.0-rc.0" + "@vuepress/plugin-git" "2.0.0-rc.0" + "@vuepress/plugin-medium-zoom" "2.0.0-rc.0" + "@vuepress/plugin-nprogress" "2.0.0-rc.0" + "@vuepress/plugin-palette" "2.0.0-rc.0" + "@vuepress/plugin-prismjs" "2.0.0-rc.0" + "@vuepress/plugin-theme-data" "2.0.0-rc.0" + "@vuepress/shared" "2.0.0-rc.0" + "@vuepress/utils" "2.0.0-rc.0" + "@vueuse/core" "^10.6.1" + sass "^1.69.5" + vue "^3.3.8" + vue-router "^4.2.5" + +"@vuepress/utils@2.0.0-rc.0": + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vuepress/utils/-/utils-2.0.0-rc.0.tgz#34331cea9d4f843cc0c5641aaa395ab49ee66041" + integrity sha512-Q1ay/woClDHcW0Qe91KsnHoupdNN0tp/vhjvVLuAYxlv/1Obii7hz9WFcajyyGEhmsYxdvG2sGmcxFA02tuKkw== + dependencies: + "@types/debug" "^4.1.12" + "@types/fs-extra" "^11.0.4" + "@types/hash-sum" "^1.0.2" + "@vuepress/shared" "2.0.0-rc.0" + debug "^4.3.4" + fs-extra "^11.1.1" + globby "^14.0.0" + hash-sum "^2.0.0" + ora "^7.0.1" + picocolors "^1.0.0" + upath "^2.0.1" + +"@vueuse/core@^10.6.1": + version "10.7.0" + resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.7.0.tgz#34f2f02f179dc0dcffc2be70d6b1233e011404b9" + integrity sha512-4EUDESCHtwu44ZWK3Gc/hZUVhVo/ysvdtwocB5vcauSV4B7NiGY5972WnsojB3vRNdxvAt7kzJWE2h9h7C9d5w== + dependencies: + "@types/web-bluetooth" "^0.0.20" + "@vueuse/metadata" "10.7.0" + "@vueuse/shared" "10.7.0" + vue-demi ">=0.14.6" + +"@vueuse/metadata@10.7.0": + version "10.7.0" + resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.7.0.tgz#7b05e6cfd376aa9bb339a81e16a89c12f3e88c03" + integrity sha512-GlaH7tKP2iBCZ3bHNZ6b0cl9g0CJK8lttkBNUX156gWvNYhTKEtbweWLm9rxCPIiwzYcr/5xML6T8ZUEt+DkvA== + +"@vueuse/shared@10.7.0": + version "10.7.0" + resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-10.7.0.tgz#21e425cc5ede421e0cda38ac59a0beee6da86b1b" + integrity sha512-kc00uV6CiaTdc3i1CDC4a3lBxzaBE9AgYNtFN87B5OOscqeWElj/uza8qVDmk7/U8JbqoONLbtqiLJ5LGRuqlw== + dependencies: + vue-demi ">=0.14.6" + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-sequence-parser@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz#e0aa1cdcbc8f8bb0b5bca625aac41f5f056973cf" + integrity sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg== + +anymatch@^3.0.0, anymatch@~3.1.2: + version "3.1.2" + resolved "https://repo.huaweicloud.com/repository/npm/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://repo.huaweicloud.com/repository/npm/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +autoprefixer@^10.4.16: + version "10.4.16" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8" + integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ== + dependencies: + browserslist "^4.21.10" + caniuse-lite "^1.0.30001538" + fraction.js "^4.3.6" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +balloon-css@^1.2.0: + version "1.2.0" + resolved "https://repo.huaweicloud.com/repository/npm/balloon-css/-/balloon-css-1.2.0.tgz#53d3fb4051264a278a58713bed6865845dbcaf4b" + integrity sha512-urXwkHgwp6GsXVF+it01485Z2Cj4pnW02ICnM0TemOlkKmCNnDLmyy+ZZiRXBpwldUXO+aRNr7Hdia4CBvXJ5A== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://repo.huaweicloud.com/repository/npm/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://repo.huaweicloud.com/repository/npm/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bl@^5.0.0: + version "5.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/bl/-/bl-5.0.0.tgz#6928804a41e9da9034868e1c50ca88f21f57aea2" + integrity sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ== + dependencies: + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^3.4.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://repo.huaweicloud.com/repository/npm/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@^4.21.10: + version "4.21.11" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.11.tgz#35f74a3e51adc4d193dcd76ea13858de7b8fecb8" + integrity sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ== + dependencies: + caniuse-lite "^1.0.30001538" + electron-to-chromium "^1.4.526" + node-releases "^2.0.13" + update-browserslist-db "^1.0.13" + +buffer@^6.0.3: + version "6.0.3" + resolved "https://repo.huaweicloud.com/repository/npm/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +cac@^6.7.12, cac@^6.7.14: + version "6.7.14" + resolved "https://repo.huaweicloud.com/repository/npm/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + +caniuse-lite@^1.0.30001538: + version "1.0.30001538" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz#9dbc6b9af1ff06b5eb12350c2012b3af56744f3f" + integrity sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw== + +chalk@^5.0.0: + version "5.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" + integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== + +chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3: + version "3.5.3" + resolved "https://repo.huaweicloud.com/repository/npm/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-spinners@^2.9.0: + version "2.9.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.1.tgz#9c0b9dad69a6d47cbb4333c14319b060ed395a35" + integrity sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ== + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://repo.huaweicloud.com/repository/npm/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +csstype@^3.1.0: + version "3.1.0" + resolved "https://repo.huaweicloud.com/repository/npm/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" + integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== + +csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +debug@^4.3.4: + version "4.3.4" + resolved "https://repo.huaweicloud.com/repository/npm/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deepmerge@^1.5.2: + version "1.5.2" + resolved "https://repo.huaweicloud.com/repository/npm/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" + integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +electron-to-chromium@^1.4.526: + version "1.4.528" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.528.tgz#7c900fd73d9d2e8bb0dab0e301f25f0f4776ef2c" + integrity sha512-UdREXMXzLkREF4jA8t89FQjA8WHI6ssP38PMY4/4KhXFQbtImnghh4GkCgrtiZwLKUKVD2iTVXvDVQjfomEQuA== + +emoji-regex@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f" + integrity sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA== + +entities@~3.0.1: + version "3.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + +envinfo@^7.11.0: + version "7.11.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" + integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== + +esbuild@^0.19.3, esbuild@~0.19.5: + version "0.19.9" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.9.tgz#423a8f35153beb22c0b695da1cd1e6c0c8cdd490" + integrity sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg== + optionalDependencies: + "@esbuild/android-arm" "0.19.9" + "@esbuild/android-arm64" "0.19.9" + "@esbuild/android-x64" "0.19.9" + "@esbuild/darwin-arm64" "0.19.9" + "@esbuild/darwin-x64" "0.19.9" + "@esbuild/freebsd-arm64" "0.19.9" + "@esbuild/freebsd-x64" "0.19.9" + "@esbuild/linux-arm" "0.19.9" + "@esbuild/linux-arm64" "0.19.9" + "@esbuild/linux-ia32" "0.19.9" + "@esbuild/linux-loong64" "0.19.9" + "@esbuild/linux-mips64el" "0.19.9" + "@esbuild/linux-ppc64" "0.19.9" + "@esbuild/linux-riscv64" "0.19.9" + "@esbuild/linux-s390x" "0.19.9" + "@esbuild/linux-x64" "0.19.9" + "@esbuild/netbsd-x64" "0.19.9" + "@esbuild/openbsd-x64" "0.19.9" + "@esbuild/sunos-x64" "0.19.9" + "@esbuild/win32-arm64" "0.19.9" + "@esbuild/win32-ia32" "0.19.9" + "@esbuild/win32-x64" "0.19.9" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://repo.huaweicloud.com/repository/npm/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://repo.huaweicloud.com/repository/npm/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://repo.huaweicloud.com/repository/npm/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fastq@^1.6.0: + version "1.13.0" + resolved "https://repo.huaweicloud.com/repository/npm/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +follow-redirects@^1.0.0: + version "1.15.1" + resolved "https://repo.huaweicloud.com/repository/npm/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + +fraction.js@^4.3.6: + version "4.3.6" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.6.tgz#e9e3acec6c9a28cf7bc36cbe35eea4ceb2c5c92d" + integrity sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg== + +fs-extra@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://repo.huaweicloud.com/repository/npm/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://repo.huaweicloud.com/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +globby@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.0.tgz#ea9c062a3614e33f516804e778590fcf055256b9" + integrity sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ== + dependencies: + "@sindresorhus/merge-streams" "^1.0.0" + fast-glob "^3.3.2" + ignore "^5.2.4" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.10" + resolved "https://repo.huaweicloud.com/repository/npm/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +gray-matter@^4.0.3: + version "4.0.3" + resolved "https://repo.huaweicloud.com/repository/npm/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" + integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== + dependencies: + js-yaml "^3.13.1" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + +hash-sum@^2.0.0: + version "2.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" + integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== + +highlight.js@^9.7.0: + version "9.18.5" + resolved "https://repo.huaweicloud.com/repository/npm/highlight.js/-/highlight.js-9.18.5.tgz#d18a359867f378c138d6819edfc2a8acd5f29825" + integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA== + +http-proxy-middleware@^1.0.0: + version "1.3.1" + resolved "https://repo.huaweicloud.com/repository/npm/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz#43700d6d9eecb7419bf086a128d0f7205d9eb665" + integrity sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg== + dependencies: + "@types/http-proxy" "^1.17.5" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://repo.huaweicloud.com/repository/npm/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://repo.huaweicloud.com/repository/npm/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +immutable@^4.0.0: + version "4.1.0" + resolved "https://repo.huaweicloud.com/repository/npm/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" + integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== + +inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://repo.huaweicloud.com/repository/npm/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://repo.huaweicloud.com/repository/npm/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-extendable@^0.1.0: + version "0.1.1" + resolved "https://repo.huaweicloud.com/repository/npm/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://repo.huaweicloud.com/repository/npm/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://repo.huaweicloud.com/repository/npm/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-unicode-supported@^1.1.0: + version "1.2.0" + resolved "https://repo.huaweicloud.com/repository/npm/is-unicode-supported/-/is-unicode-supported-1.2.0.tgz#f4f54f34d8ebc84a46b93559a036763b6d3e1014" + integrity sha512-wH+U77omcRzevfIG8dDhTS0V9zZyweakfD01FULl97+0EHiJTTZtJqxPSkIIo/SDPv/i07k/C9jAPY+jwLLeUQ== + +is-unicode-supported@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +javascript-stringify@^2.0.1: + version "2.1.0" + resolved "https://repo.huaweicloud.com/repository/npm/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" + integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://repo.huaweicloud.com/repository/npm/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsonc-parser@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://repo.huaweicloud.com/repository/npm/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://repo.huaweicloud.com/repository/npm/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +lilconfig@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +linkify-it@^4.0.1: + version "4.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec" + integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw== + dependencies: + uc.micro "^1.0.1" + +log-symbols@^5.1.0: + version "5.1.0" + resolved "https://repo.huaweicloud.com/repository/npm/log-symbols/-/log-symbols-5.1.0.tgz#a20e3b9a5f53fac6aeb8e2bb22c07cf2c8f16d93" + integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== + dependencies: + chalk "^5.0.0" + is-unicode-supported "^1.1.0" + +magic-string@^0.30.5: + version "0.30.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" + integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + +markdown-it-anchor@*: + version "8.6.4" + resolved "https://repo.huaweicloud.com/repository/npm/markdown-it-anchor/-/markdown-it-anchor-8.6.4.tgz#affb8aa0910a504c114e9fcad53ac3a5b907b0e6" + integrity sha512-Ul4YVYZNxMJYALpKtu+ZRdrryYt/GlQ5CK+4l1bp/gWXOG2QWElt6AqF3Mih/wfUKdZbNAZVXGR73/n6U/8img== + +markdown-it-anchor@^8.6.7: + version "8.6.7" + resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz#ee6926daf3ad1ed5e4e3968b1740eef1c6399634" + integrity sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA== + +markdown-it-container@^3.0.0: + version "3.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/markdown-it-container/-/markdown-it-container-3.0.0.tgz#1d19b06040a020f9a827577bb7dbf67aa5de9a5b" + integrity sha512-y6oKTq4BB9OQuY/KLfk/O3ysFhB3IMYoIWhGJEidXt1NQFocFK2sA2t0NYZAMyMShAGL6x5OPIbrmXPIqaN9rw== + +markdown-it-emoji@^2.0.2: + version "2.0.2" + resolved "https://repo.huaweicloud.com/repository/npm/markdown-it-emoji/-/markdown-it-emoji-2.0.2.tgz#cd42421c2fda1537d9cc12b9923f5c8aeb9029c8" + integrity sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ== + +markdown-it@^13.0.1: + version "13.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/markdown-it/-/markdown-it-13.0.1.tgz#c6ecc431cacf1a5da531423fc6a42807814af430" + integrity sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q== + dependencies: + argparse "^2.0.1" + entities "~3.0.1" + linkify-it "^4.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +markdown-it@^13.0.2: + version "13.0.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536" + integrity sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w== + dependencies: + argparse "^2.0.1" + entities "~3.0.1" + linkify-it "^4.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + +medium-zoom@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/medium-zoom/-/medium-zoom-1.1.0.tgz#6efb6bbda861a02064ee71a2617a8dc4381ecc71" + integrity sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://repo.huaweicloud.com/repository/npm/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.5" + resolved "https://repo.huaweicloud.com/repository/npm/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://repo.huaweicloud.com/repository/npm/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +ms@2.1.2: + version "2.1.2" + resolved "https://repo.huaweicloud.com/repository/npm/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nanoid@^3.3.4: + version "3.3.4" + resolved "https://repo.huaweicloud.com/repository/npm/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://repo.huaweicloud.com/repository/npm/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://repo.huaweicloud.com/repository/npm/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://repo.huaweicloud.com/repository/npm/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +ora@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-7.0.1.tgz#cdd530ecd865fe39e451a0e7697865669cb11930" + integrity sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw== + dependencies: + chalk "^5.3.0" + cli-cursor "^4.0.0" + cli-spinners "^2.9.0" + is-interactive "^2.0.0" + is-unicode-supported "^1.3.0" + log-symbols "^5.1.0" + stdin-discarder "^0.1.0" + string-width "^6.1.0" + strip-ansi "^7.1.0" + +path-key@^3.1.0: + version "3.1.1" + resolved "https://repo.huaweicloud.com/repository/npm/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-type@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" + integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +postcss-load-config@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.1.tgz#152383f481c2758274404e4962743191d73875bd" + integrity sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA== + dependencies: + lilconfig "^2.0.5" + yaml "^2.1.1" + +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://repo.huaweicloud.com/repository/npm/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.14: + version "8.4.16" + resolved "https://repo.huaweicloud.com/repository/npm/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c" + integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +postcss@^8.4.31, postcss@^8.4.32: + version "8.4.32" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.32.tgz#1dac6ac51ab19adb21b8b34fd2d93a86440ef6c9" + integrity sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prismjs@^1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://repo.huaweicloud.com/repository/npm/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +readable-stream@^3.4.0: + version "3.6.0" + resolved "https://repo.huaweicloud.com/repository/npm/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://repo.huaweicloud.com/repository/npm/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://repo.huaweicloud.com/repository/npm/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rollup@^4.2.0, rollup@^4.4.1: + version "4.9.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.1.tgz#351d6c03e4e6bcd7a0339df3618d2aeeb108b507" + integrity sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw== + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.9.1" + "@rollup/rollup-android-arm64" "4.9.1" + "@rollup/rollup-darwin-arm64" "4.9.1" + "@rollup/rollup-darwin-x64" "4.9.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.9.1" + "@rollup/rollup-linux-arm64-gnu" "4.9.1" + "@rollup/rollup-linux-arm64-musl" "4.9.1" + "@rollup/rollup-linux-riscv64-gnu" "4.9.1" + "@rollup/rollup-linux-x64-gnu" "4.9.1" + "@rollup/rollup-linux-x64-musl" "4.9.1" + "@rollup/rollup-win32-arm64-msvc" "4.9.1" + "@rollup/rollup-win32-ia32-msvc" "4.9.1" + "@rollup/rollup-win32-x64-msvc" "4.9.1" + fsevents "~2.3.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://repo.huaweicloud.com/repository/npm/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://repo.huaweicloud.com/repository/npm/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +sass@^1.69.5: + version "1.69.5" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.5.tgz#23e18d1c757a35f2e52cc81871060b9ad653dfde" + integrity sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + +section-matter@^1.0.0: + version "1.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" + integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + dependencies: + extend-shallow "^2.0.1" + kind-of "^6.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shiki@^0.14.5: + version "0.14.7" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.7.tgz#c3c9e1853e9737845f1d2ef81b31bcfb07056d4e" + integrity sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg== + dependencies: + ansi-sequence-parser "^1.1.0" + jsonc-parser "^3.2.0" + vscode-oniguruma "^1.7.0" + vscode-textmate "^8.0.0" + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://repo.huaweicloud.com/repository/npm/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +slash@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== + +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: + version "1.0.2" + resolved "https://repo.huaweicloud.com/repository/npm/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://repo.huaweicloud.com/repository/npm/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.4" + resolved "https://repo.huaweicloud.com/repository/npm/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://repo.huaweicloud.com/repository/npm/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stdin-discarder@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21" + integrity sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ== + dependencies: + bl "^5.0.0" + +string-width@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-6.1.0.tgz#96488d6ed23f9ad5d82d13522af9e4c4c3fd7518" + integrity sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^10.2.1" + strip-ansi "^7.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://repo.huaweicloud.com/repository/npm/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^7.0.1: + version "7.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" + integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== + dependencies: + ansi-regex "^6.0.1" + +strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-debounce@^4.0.0: + version "4.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/ts-debounce/-/ts-debounce-4.0.0.tgz#33440ef64fab53793c3d546a8ca6ae539ec15841" + integrity sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg== + +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://repo.huaweicloud.com/repository/npm/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://repo.huaweicloud.com/repository/npm/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +upath@^2.0.1: + version "2.0.1" + resolved "https://repo.huaweicloud.com/repository/npm/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" + integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://repo.huaweicloud.com/repository/npm/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +vite@~5.0.0: + version "5.0.10" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.10.tgz#1e13ef5c3cf5aa4eed81f5df6d107b3c3f1f6356" + integrity sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw== + dependencies: + esbuild "^0.19.3" + postcss "^8.4.32" + rollup "^4.2.0" + optionalDependencies: + fsevents "~2.3.3" + +vscode-oniguruma@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" + integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== + +vscode-textmate@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" + integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== + +vue-demi@>=0.14.6: + version "0.14.6" + resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.6.tgz#dc706582851dc1cdc17a0054f4fec2eb6df74c92" + integrity sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w== + +vue-router@^4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.2.5.tgz#b9e3e08f1bd9ea363fdd173032620bc50cf0e98a" + integrity sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw== + dependencies: + "@vue/devtools-api" "^6.5.0" + +vue@^2.6.14: + version "2.7.10" + resolved "https://repo.huaweicloud.com/repository/npm/vue/-/vue-2.7.10.tgz#ae516cc6c88e1c424754468844218fdd5e280f40" + integrity sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg== + dependencies: + "@vue/compiler-sfc" "2.7.10" + csstype "^3.1.0" + +vue@^3.3.8: + version "3.3.12" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.3.12.tgz#4a3a39e79d22e9826ae7c058863316333c838b63" + integrity sha512-jYNv2QmET2OTHsFzfWHMnqgCfqL4zfo97QwofdET+GBRCHhSCHuMTTvNIgeSn0/xF3JRT5OGah6MDwUFN7MPlg== + dependencies: + "@vue/compiler-dom" "3.3.12" + "@vue/compiler-sfc" "3.3.12" + "@vue/runtime-dom" "3.3.12" + "@vue/server-renderer" "3.3.12" + "@vue/shared" "3.3.12" + +vuepress-vite@2.0.0-rc.0: + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/vuepress-vite/-/vuepress-vite-2.0.0-rc.0.tgz#edcb902881368143c24199e66c2300f4bcd2b61f" + integrity sha512-+2XBejeiskPyr2raBeA2o4uDFDsjtadpUVmtio3qqFtQpOhidz/ORuiTLr2UfLtFn1ASIHP6Vy2YjQ0e/TeUVw== + dependencies: + "@vuepress/bundler-vite" "2.0.0-rc.0" + "@vuepress/cli" "2.0.0-rc.0" + "@vuepress/core" "2.0.0-rc.0" + "@vuepress/theme-default" "2.0.0-rc.0" + vue "^3.3.8" + +vuepress@2.0.0-rc.0: + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/vuepress/-/vuepress-2.0.0-rc.0.tgz#680f15968afd8ba0e27c4af52d15109eb11bb1b4" + integrity sha512-sydt/B7+pIw926G5PntYmptLkC5o2buXKh+WR1+P2KnsvkXU+UGnQrJJ0FBvu/4RNuY99tkUZd59nyPhEmRrCg== + dependencies: + vuepress-vite "2.0.0-rc.0" + +webpack-chain@^6.5.1: + version "6.5.1" + resolved "https://repo.huaweicloud.com/repository/npm/webpack-chain/-/webpack-chain-6.5.1.tgz#4f27284cbbb637e3c8fbdef43eef588d4d861206" + integrity sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA== + dependencies: + deepmerge "^1.5.2" + javascript-stringify "^2.0.1" + +which@^2.0.1: + version "2.0.2" + resolved "https://repo.huaweicloud.com/repository/npm/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +yaml@^2.1.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144" + integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg== diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..3e7c852 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,25 @@ +# Project Configuration +project.name=KavaRef +project.url=https://github.com/HighCapable/KavaRef +project.groupName=com.highcapable.kavaref +project.kavaref-core.version="1.0.0" +project.kavaref-extension.version="1.0.0" +project.samples-kavaref-demo.groupName=${project.groupName}.demo +project.samples-kavaref-demo.version=universal +# Maven Publish Configuration +SONATYPE_HOST=CENTRAL_PORTAL +RELEASE_SIGNING_ENABLED=true +# Maven POM Configuration +POM_NAME=KavaRef +POM_DESCRIPTION=A modernizing Java Reflection with Kotlin. +POM_URL=https://github.com/HighCapable/KavaRef +POM_LICENSE_NAME=Apache License 2.0 +POM_LICENSE_URL=https://github.com/HighCapable/KavaRef/blob/main/LICENSE +POM_LICENSE_DIST=repo +POM_SCM_URL=https://github.com/HighCapable/KavaRef +POM_SCM_CONNECTION=scm:git:git://github.com/HighCapable/KavaRef.git +POM_SCM_DEV_CONNECTION=scm:git:ssh://github.com/HighCapable/KavaRef.git +POM_DEVELOPER_ID=0 +POM_DEVELOPER_NAME=fankes +POM_DEVELOPER_EMAIL=qzmmcn@163.com +POM_DEVELOPER_URL=https://github.com/fankes \ No newline at end of file diff --git a/gradle/sweet-dependency/sweet-dependency-config.yaml b/gradle/sweet-dependency/sweet-dependency-config.yaml new file mode 100644 index 0000000..7ad2881 --- /dev/null +++ b/gradle/sweet-dependency/sweet-dependency-config.yaml @@ -0,0 +1,28 @@ +preferences: + autowire-on-sync-mode: UPDATE_OPTIONAL_DEPENDENCIES + repositories-mode: FAIL_ON_PROJECT_REPOS + +repositories: + gradle-plugin-portal: + scope: PLUGINS + google: + maven-central: + +plugins: + org.jetbrains.kotlin.jvm: + alias: kotlin-jvm + version: 2.1.21 + org.jetbrains.dokka: + alias: kotlin-dokka + version: 1.9.20 + auto-update: false + com.vanniktech.maven.publish: + alias: maven-publish + version: 0.33.0 + +libraries: + org.slf4j: + slf4j-api: + version: 2.0.17 + slf4j-simple: + version-ref: ::slf4j-api \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 zcmb5WV{~QRw(p$^Dz@00IL3?^hro$gg*4VI_WAaTyVM5Foj~O|-84 z$;06hMwt*rV;^8iB z1~&0XWpYJmG?Ts^K9PC62H*`G}xom%S%yq|xvG~FIfP=9*f zZoDRJBm*Y0aId=qJ?7dyb)6)JGWGwe)MHeNSzhi)Ko6J<-m@v=a%NsP537lHe0R* z`If4$aaBA#S=w!2z&m>{lpTy^Lm^mg*3?M&7HFv}7K6x*cukLIGX;bQG|QWdn{%_6 zHnwBKr84#B7Z+AnBXa16a?or^R?+>$4`}{*a_>IhbjvyTtWkHw)|ay)ahWUd-qq$~ zMbh6roVsj;_qnC-R{G+Cy6bApVOinSU-;(DxUEl!i2)1EeQ9`hrfqj(nKI7?Z>Xur zoJz-a`PxkYit1HEbv|jy%~DO^13J-ut986EEG=66S}D3!L}Efp;Bez~7tNq{QsUMm zh9~(HYg1pA*=37C0}n4g&bFbQ+?-h-W}onYeE{q;cIy%eZK9wZjSwGvT+&Cgv z?~{9p(;bY_1+k|wkt_|N!@J~aoY@|U_RGoWX<;p{Nu*D*&_phw`8jYkMNpRTWx1H* z>J-Mi_!`M468#5Aix$$u1M@rJEIOc?k^QBc?T(#=n&*5eS#u*Y)?L8Ha$9wRWdH^3D4|Ps)Y?m0q~SiKiSfEkJ!=^`lJ(%W3o|CZ zSrZL-Xxc{OrmsQD&s~zPfNJOpSZUl%V8tdG%ei}lQkM+z@-4etFPR>GOH9+Y_F<3=~SXln9Kb-o~f>2a6Xz@AS3cn^;c_>lUwlK(n>z?A>NbC z`Ud8^aQy>wy=$)w;JZzA)_*Y$Z5hU=KAG&htLw1Uh00yE!|Nu{EZkch zY9O6x7Y??>!7pUNME*d!=R#s)ghr|R#41l!c?~=3CS8&zr6*aA7n9*)*PWBV2w+&I zpW1-9fr3j{VTcls1>ua}F*bbju_Xq%^v;-W~paSqlf zolj*dt`BBjHI)H9{zrkBo=B%>8}4jeBO~kWqO!~Thi!I1H(in=n^fS%nuL=X2+s!p}HfTU#NBGiwEBF^^tKU zbhhv+0dE-sbK$>J#t-J!B$TMgN@Wh5wTtK2BG}4BGfsZOoRUS#G8Cxv|6EI*n&Xxq zt{&OxCC+BNqz$9b0WM7_PyBJEVObHFh%%`~!@MNZlo*oXDCwDcFwT~Rls!aApL<)^ zbBftGKKBRhB!{?fX@l2_y~%ygNFfF(XJzHh#?`WlSL{1lKT*gJM zs>bd^H9NCxqxn(IOky5k-wALFowQr(gw%|`0991u#9jXQh?4l|l>pd6a&rx|v=fPJ z1mutj{YzpJ_gsClbWFk(G}bSlFi-6@mwoQh-XeD*j@~huW4(8ub%^I|azA)h2t#yG z7e_V_<4jlM3D(I+qX}yEtqj)cpzN*oCdYHa!nm%0t^wHm)EmFP*|FMw!tb@&`G-u~ zK)=Sf6z+BiTAI}}i{*_Ac$ffr*Wrv$F7_0gJkjx;@)XjYSh`RjAgrCck`x!zP>Ifu z&%he4P|S)H*(9oB4uvH67^0}I-_ye_!w)u3v2+EY>eD3#8QR24<;7?*hj8k~rS)~7 zSXs5ww)T(0eHSp$hEIBnW|Iun<_i`}VE0Nc$|-R}wlSIs5pV{g_Dar(Zz<4X3`W?K z6&CAIl4U(Qk-tTcK{|zYF6QG5ArrEB!;5s?tW7 zrE3hcFY&k)+)e{+YOJ0X2uDE_hd2{|m_dC}kgEKqiE9Q^A-+>2UonB+L@v3$9?AYw zVQv?X*pK;X4Ovc6Ev5Gbg{{Eu*7{N3#0@9oMI~}KnObQE#Y{&3mM4`w%wN+xrKYgD zB-ay0Q}m{QI;iY`s1Z^NqIkjrTlf`B)B#MajZ#9u41oRBC1oM1vq0i|F59> z#StM@bHt|#`2)cpl_rWB($DNJ3Lap}QM-+A$3pe}NyP(@+i1>o^fe-oxX#Bt`mcQc zb?pD4W%#ep|3%CHAYnr*^M6Czg>~L4?l16H1OozM{P*en298b+`i4$|w$|4AHbzqB zHpYUsHZET$Z0ztC;U+0*+amF!@PI%^oUIZy{`L{%O^i{Xk}X0&nl)n~tVEpcAJSJ} zverw15zP1P-O8h9nd!&hj$zuwjg?DoxYIw{jWM zW5_pj+wFy8Tsa9g<7Qa21WaV&;ejoYflRKcz?#fSH_)@*QVlN2l4(QNk| z4aPnv&mrS&0|6NHq05XQw$J^RR9T{3SOcMKCXIR1iSf+xJ0E_Wv?jEc*I#ZPzyJN2 zUG0UOXHl+PikM*&g$U@g+KbG-RY>uaIl&DEtw_Q=FYq?etc!;hEC_}UX{eyh%dw2V zTTSlap&5>PY{6I#(6`j-9`D&I#|YPP8a;(sOzgeKDWsLa!i-$frD>zr-oid!Hf&yS z!i^cr&7tN}OOGmX2)`8k?Tn!!4=tz~3hCTq_9CdiV!NIblUDxHh(FJ$zs)B2(t5@u z-`^RA1ShrLCkg0)OhfoM;4Z{&oZmAec$qV@ zGQ(7(!CBk<5;Ar%DLJ0p0!ResC#U<+3i<|vib1?{5gCebG7$F7URKZXuX-2WgF>YJ^i zMhHDBsh9PDU8dlZ$yJKtc6JA#y!y$57%sE>4Nt+wF1lfNIWyA`=hF=9Gj%sRwi@vd z%2eVV3y&dvAgyuJ=eNJR+*080dbO_t@BFJO<@&#yqTK&+xc|FRR;p;KVk@J3$S{p` zGaMj6isho#%m)?pOG^G0mzOAw0z?!AEMsv=0T>WWcE>??WS=fII$t$(^PDPMU(P>o z_*0s^W#|x)%tx8jIgZY~A2yG;US0m2ZOQt6yJqW@XNY_>_R7(Nxb8Ged6BdYW6{prd!|zuX$@Q2o6Ona8zzYC1u!+2!Y$Jc9a;wy+pXt}o6~Bu1oF1c zp7Y|SBTNi@=I(K%A60PMjM#sfH$y*c{xUgeSpi#HB`?|`!Tb&-qJ3;vxS!TIzuTZs-&%#bAkAyw9m4PJgvey zM5?up*b}eDEY+#@tKec)-c(#QF0P?MRlD1+7%Yk*jW;)`f;0a-ZJ6CQA?E%>i2Dt7T9?s|9ZF|KP4;CNWvaVKZ+Qeut;Jith_y{v*Ny6Co6!8MZx;Wgo z=qAi%&S;8J{iyD&>3CLCQdTX*$+Rx1AwA*D_J^0>suTgBMBb=*hefV+Ars#mmr+YsI3#!F@Xc1t4F-gB@6aoyT+5O(qMz*zG<9Qq*f0w^V!03rpr*-WLH}; zfM{xSPJeu6D(%8HU%0GEa%waFHE$G?FH^kMS-&I3)ycx|iv{T6Wx}9$$D&6{%1N_8 z_CLw)_9+O4&u94##vI9b-HHm_95m)fa??q07`DniVjAy`t7;)4NpeyAY(aAk(+T_O z1om+b5K2g_B&b2DCTK<>SE$Ode1DopAi)xaJjU>**AJK3hZrnhEQ9E`2=|HHe<^tv z63e(bn#fMWuz>4erc47}!J>U58%<&N<6AOAewyzNTqi7hJc|X{782&cM zHZYclNbBwU6673=!ClmxMfkC$(CykGR@10F!zN1Se83LR&a~$Ht&>~43OX22mt7tcZUpa;9@q}KDX3O&Ugp6< zLZLfIMO5;pTee1vNyVC$FGxzK2f>0Z-6hM82zKg44nWo|n}$Zk6&;5ry3`(JFEX$q zK&KivAe${e^5ZGc3a9hOt|!UOE&OocpVryE$Y4sPcs4rJ>>Kbi2_subQ9($2VN(3o zb~tEzMsHaBmBtaHAyES+d3A(qURgiskSSwUc9CfJ@99&MKp2sooSYZu+-0t0+L*!I zYagjOlPgx|lep9tiU%ts&McF6b0VE57%E0Ho%2oi?=Ks+5%aj#au^OBwNwhec zta6QAeQI^V!dF1C)>RHAmB`HnxyqWx?td@4sd15zPd*Fc9hpDXP23kbBenBxGeD$k z;%0VBQEJ-C)&dTAw_yW@k0u?IUk*NrkJ)(XEeI z9Y>6Vel>#s_v@=@0<{4A{pl=9cQ&Iah0iD0H`q)7NeCIRz8zx;! z^OO;1+IqoQNak&pV`qKW+K0^Hqp!~gSohcyS)?^P`JNZXw@gc6{A3OLZ?@1Uc^I2v z+X!^R*HCm3{7JPq{8*Tn>5;B|X7n4QQ0Bs79uTU%nbqOJh`nX(BVj!#f;#J+WZxx4 z_yM&1Y`2XzhfqkIMO7tB3raJKQS+H5F%o83bM+hxbQ zeeJm=Dvix$2j|b4?mDacb67v-1^lTp${z=jc1=j~QD>7c*@+1?py>%Kj%Ejp7Y-!? z8iYRUlGVrQPandAaxFfks53@2EC#0)%mrnmGRn&>=$H$S8q|kE_iWko4`^vCS2aWg z#!`RHUGyOt*k?bBYu3*j3u0gB#v(3tsije zgIuNNWNtrOkx@Pzs;A9un+2LX!zw+p3_NX^Sh09HZAf>m8l@O*rXy_82aWT$Q>iyy zqO7Of)D=wcSn!0+467&!Hl))eff=$aneB?R!YykdKW@k^_uR!+Q1tR)+IJb`-6=jj zymzA>Sv4>Z&g&WWu#|~GcP7qP&m*w-S$)7Xr;(duqCTe7p8H3k5>Y-n8438+%^9~K z3r^LIT_K{i7DgEJjIocw_6d0!<;wKT`X;&vv+&msmhAAnIe!OTdybPctzcEzBy88_ zWO{6i4YT%e4^WQZB)KHCvA(0tS zHu_Bg+6Ko%a9~$EjRB90`P(2~6uI@SFibxct{H#o&y40MdiXblu@VFXbhz>Nko;7R z70Ntmm-FePqhb%9gL+7U8@(ch|JfH5Fm)5${8|`Lef>LttM_iww6LW2X61ldBmG0z zax3y)njFe>j*T{i0s8D4=L>X^j0)({R5lMGVS#7(2C9@AxL&C-lZQx~czI7Iv+{%1 z2hEG>RzX4S8x3v#9sgGAnPzptM)g&LB}@%E>fy0vGSa(&q0ch|=ncKjNrK z`jA~jObJhrJ^ri|-)J^HUyeZXz~XkBp$VhcTEcTdc#a2EUOGVX?@mYx#Vy*!qO$Jv zQ4rgOJ~M*o-_Wptam=~krnmG*p^j!JAqoQ%+YsDFW7Cc9M%YPiBOrVcD^RY>m9Pd< zu}#9M?K{+;UIO!D9qOpq9yxUquQRmQNMo0pT`@$pVt=rMvyX)ph(-CCJLvUJy71DI zBk7oc7)-%ngdj~s@76Yse3L^gV0 z2==qfp&Q~L(+%RHP0n}+xH#k(hPRx(!AdBM$JCfJ5*C=K3ts>P?@@SZ_+{U2qFZb>4kZ{Go37{# zSQc+-dq*a-Vy4?taS&{Ht|MLRiS)Sn14JOONyXqPNnpq&2y~)6wEG0oNy>qvod$FF z`9o&?&6uZjhZ4_*5qWVrEfu(>_n2Xi2{@Gz9MZ8!YmjYvIMasE9yVQL10NBrTCczq zcTY1q^PF2l!Eraguf{+PtHV3=2A?Cu&NN&a8V(y;q(^_mFc6)%Yfn&X&~Pq zU1?qCj^LF(EQB1F`8NxNjyV%fde}dEa(Hx=r7$~ts2dzDwyi6ByBAIx$NllB4%K=O z$AHz1<2bTUb>(MCVPpK(E9wlLElo(aSd(Os)^Raum`d(g9Vd_+Bf&V;l=@mM=cC>) z)9b0enb)u_7V!!E_bl>u5nf&Rl|2r=2F3rHMdb7y9E}}F82^$Rf+P8%dKnOeKh1vs zhH^P*4Ydr^$)$h@4KVzxrHyy#cKmWEa9P5DJ|- zG;!Qi35Tp7XNj60=$!S6U#!(${6hyh7d4q=pF{`0t|N^|L^d8pD{O9@tF~W;#Je*P z&ah%W!KOIN;SyAEhAeTafJ4uEL`(RtnovM+cb(O#>xQnk?dzAjG^~4$dFn^<@-Na3 z395;wBnS{t*H;Jef2eE!2}u5Ns{AHj>WYZDgQJt8v%x?9{MXqJsGP|l%OiZqQ1aB! z%E=*Ig`(!tHh>}4_z5IMpg{49UvD*Pp9!pxt_gdAW%sIf3k6CTycOT1McPl=_#0?8 zVjz8Hj*Vy9c5-krd-{BQ{6Xy|P$6LJvMuX$* zA+@I_66_ET5l2&gk9n4$1M3LN8(yEViRx&mtd#LD}AqEs?RW=xKC(OCWH;~>(X6h!uDxXIPH06xh z*`F4cVlbDP`A)-fzf>MuScYsmq&1LUMGaQ3bRm6i7OsJ|%uhTDT zlvZA1M}nz*SalJWNT|`dBm1$xlaA>CCiQ zK`xD-RuEn>-`Z?M{1%@wewf#8?F|(@1e0+T4>nmlSRrNK5f)BJ2H*$q(H>zGD0>eL zQ!tl_Wk)k*e6v^m*{~A;@6+JGeWU-q9>?+L_#UNT%G?4&BnOgvm9@o7l?ov~XL+et zbGT)|G7)KAeqb=wHSPk+J1bdg7N3$vp(ekjI1D9V$G5Cj!=R2w=3*4!z*J-r-cyeb zd(i2KmX!|Lhey!snRw z?#$Gu%S^SQEKt&kep)up#j&9}e+3=JJBS(s>MH+|=R(`8xK{mmndWo_r`-w1#SeRD&YtAJ#GiVI*TkQZ}&aq<+bU2+coU3!jCI6E+Ad_xFW*ghnZ$q zAoF*i&3n1j#?B8x;kjSJD${1jdRB;)R*)Ao!9bd|C7{;iqDo|T&>KSh6*hCD!rwv= zyK#F@2+cv3=|S1Kef(E6Niv8kyLVLX&e=U;{0x{$tDfShqkjUME>f8d(5nzSkY6@! z^-0>DM)wa&%m#UF1F?zR`8Y3X#tA!*7Q$P3lZJ%*KNlrk_uaPkxw~ zxZ1qlE;Zo;nb@!SMazSjM>;34ROOoygo%SF);LL>rRonWwR>bmSd1XD^~sGSu$Gg# zFZ`|yKU0%!v07dz^v(tY%;So(e`o{ZYTX`hm;@b0%8|H>VW`*cr8R%3n|ehw2`(9B+V72`>SY}9^8oh$En80mZK9T4abVG*to;E z1_S6bgDOW?!Oy1LwYy=w3q~KKdbNtyH#d24PFjX)KYMY93{3-mPP-H>@M-_>N~DDu zENh~reh?JBAK=TFN-SfDfT^=+{w4ea2KNWXq2Y<;?(gf(FgVp8Zp-oEjKzB%2Iqj;48GmY3h=bcdYJ}~&4tS`Q1sb=^emaW$IC$|R+r-8V- zf0$gGE(CS_n4s>oicVk)MfvVg#I>iDvf~Ov8bk}sSxluG!6#^Z_zhB&U^`eIi1@j( z^CK$z^stBHtaDDHxn+R;3u+>Lil^}fj?7eaGB z&5nl^STqcaBxI@v>%zG|j))G(rVa4aY=B@^2{TFkW~YP!8!9TG#(-nOf^^X-%m9{Z zCC?iC`G-^RcBSCuk=Z`(FaUUe?hf3{0C>>$?Vs z`2Uud9M+T&KB6o4o9kvdi^Q=Bw!asPdxbe#W-Oaa#_NP(qpyF@bVxv5D5))srkU#m zj_KA+#7sqDn*Ipf!F5Byco4HOSd!Ui$l94|IbW%Ny(s1>f4|Mv^#NfB31N~kya9!k zWCGL-$0ZQztBate^fd>R!hXY_N9ZjYp3V~4_V z#eB)Kjr8yW=+oG)BuNdZG?jaZlw+l_ma8aET(s+-x+=F-t#Qoiuu1i`^x8Sj>b^U} zs^z<()YMFP7CmjUC@M=&lA5W7t&cxTlzJAts*%PBDAPuqcV5o7HEnqjif_7xGt)F% zGx2b4w{@!tE)$p=l3&?Bf#`+!-RLOleeRk3 z7#pF|w@6_sBmn1nECqdunmG^}pr5(ZJQVvAt$6p3H(16~;vO>?sTE`Y+mq5YP&PBo zvq!7#W$Gewy`;%6o^!Dtjz~x)T}Bdk*BS#=EY=ODD&B=V6TD2z^hj1m5^d6s)D*wk zu$z~D7QuZ2b?5`p)E8e2_L38v3WE{V`bVk;6fl#o2`) z99JsWhh?$oVRn@$S#)uK&8DL8>An0&S<%V8hnGD7Z^;Y(%6;^9!7kDQ5bjR_V+~wp zfx4m3z6CWmmZ<8gDGUyg3>t8wgJ5NkkiEm^(sedCicP^&3D%}6LtIUq>mXCAt{9eF zNXL$kGcoUTf_Lhm`t;hD-SE)m=iBnxRU(NyL}f6~1uH)`K!hmYZjLI%H}AmEF5RZt z06$wn63GHnApHXZZJ}s^s)j9(BM6e*7IBK6Bq(!)d~zR#rbxK9NVIlgquoMq z=eGZ9NR!SEqP6=9UQg#@!rtbbSBUM#ynF);zKX+|!Zm}*{H z+j=d?aZ2!?@EL7C~%B?6ouCKLnO$uWn;Y6Xz zX8dSwj732u(o*U3F$F=7xwxm>E-B+SVZH;O-4XPuPkLSt_?S0)lb7EEg)Mglk0#eS z9@jl(OnH4juMxY+*r03VDfPx_IM!Lmc(5hOI;`?d37f>jPP$?9jQQIQU@i4vuG6MagEoJrQ=RD7xt@8E;c zeGV*+Pt+t$@pt!|McETOE$9k=_C!70uhwRS9X#b%ZK z%q(TIUXSS^F0`4Cx?Rk07C6wI4!UVPeI~-fxY6`YH$kABdOuiRtl73MqG|~AzZ@iL&^s?24iS;RK_pdlWkhcF z@Wv-Om(Aealfg)D^adlXh9Nvf~Uf@y;g3Y)i(YP zEXDnb1V}1pJT5ZWyw=1i+0fni9yINurD=EqH^ciOwLUGi)C%Da)tyt=zq2P7pV5-G zR7!oq28-Fgn5pW|nlu^b!S1Z#r7!Wtr{5J5PQ>pd+2P7RSD?>(U7-|Y z7ZQ5lhYIl_IF<9?T9^IPK<(Hp;l5bl5tF9>X-zG14_7PfsA>6<$~A338iYRT{a@r_ zuXBaT=`T5x3=s&3=RYx6NgG>No4?5KFBVjE(swfcivcIpPQFx5l+O;fiGsOrl5teR z_Cm+;PW}O0Dwe_(4Z@XZ)O0W-v2X><&L*<~*q3dg;bQW3g7)a#3KiQP>+qj|qo*Hk z?57>f2?f@`=Fj^nkDKeRkN2d$Z@2eNKpHo}ksj-$`QKb6n?*$^*%Fb3_Kbf1(*W9K>{L$mud2WHJ=j0^=g30Xhg8$#g^?36`p1fm;;1@0Lrx+8t`?vN0ZorM zSW?rhjCE8$C|@p^sXdx z|NOHHg+fL;HIlqyLp~SSdIF`TnSHehNCU9t89yr@)FY<~hu+X`tjg(aSVae$wDG*C zq$nY(Y494R)hD!i1|IIyP*&PD_c2FPgeY)&mX1qujB1VHPG9`yFQpLFVQ0>EKS@Bp zAfP5`C(sWGLI?AC{XEjLKR4FVNw(4+9b?kba95ukgR1H?w<8F7)G+6&(zUhIE5Ef% z=fFkL3QKA~M@h{nzjRq!Y_t!%U66#L8!(2-GgFxkD1=JRRqk=n%G(yHKn%^&$dW>; zSjAcjETMz1%205se$iH_)ZCpfg_LwvnsZQAUCS#^FExp8O4CrJb6>JquNV@qPq~3A zZ<6dOU#6|8+fcgiA#~MDmcpIEaUO02L5#T$HV0$EMD94HT_eXLZ2Zi&(! z&5E>%&|FZ`)CN10tM%tLSPD*~r#--K(H-CZqIOb99_;m|D5wdgJ<1iOJz@h2Zkq?} z%8_KXb&hf=2Wza(Wgc;3v3TN*;HTU*q2?#z&tLn_U0Nt!y>Oo>+2T)He6%XuP;fgn z-G!#h$Y2`9>Jtf}hbVrm6D70|ERzLAU>3zoWhJmjWfgM^))T+2u$~5>HF9jQDkrXR z=IzX36)V75PrFjkQ%TO+iqKGCQ-DDXbaE;C#}!-CoWQx&v*vHfyI>$HNRbpvm<`O( zlx9NBWD6_e&J%Ous4yp~s6)Ghni!I6)0W;9(9$y1wWu`$gs<$9Mcf$L*piP zPR0Av*2%ul`W;?-1_-5Zy0~}?`e@Y5A&0H!^ApyVTT}BiOm4GeFo$_oPlDEyeGBbh z1h3q&Dx~GmUS|3@4V36&$2uO8!Yp&^pD7J5&TN{?xphf*-js1fP?B|`>p_K>lh{ij zP(?H%e}AIP?_i^f&Li=FDSQ`2_NWxL+BB=nQr=$ zHojMlXNGauvvwPU>ZLq!`bX-5F4jBJ&So{kE5+ms9UEYD{66!|k~3vsP+mE}x!>%P za98bAU0!h0&ka4EoiDvBM#CP#dRNdXJcb*(%=<(g+M@<)DZ!@v1V>;54En?igcHR2 zhubQMq}VSOK)onqHfczM7YA@s=9*ow;k;8)&?J3@0JiGcP! zP#00KZ1t)GyZeRJ=f0^gc+58lc4Qh*S7RqPIC6GugG1gXe$LIQMRCo8cHf^qXgAa2 z`}t>u2Cq1CbSEpLr~E=c7~=Qkc9-vLE%(v9N*&HF`(d~(0`iukl5aQ9u4rUvc8%m) zr2GwZN4!s;{SB87lJB;veebPmqE}tSpT>+`t?<457Q9iV$th%i__Z1kOMAswFldD6 ztbOvO337S5o#ZZgN2G99_AVqPv!?Gmt3pzgD+Hp3QPQ`9qJ(g=kjvD+fUSS3upJn! zqoG7acIKEFRX~S}3|{EWT$kdz#zrDlJU(rPkxjws_iyLKU8+v|*oS_W*-guAb&Pj1 z35Z`3z<&Jb@2Mwz=KXucNYdY#SNO$tcVFr9KdKm|%^e-TXzs6M`PBper%ajkrIyUe zp$vVxVs9*>Vp4_1NC~Zg)WOCPmOxI1V34QlG4!aSFOH{QqSVq1^1)- z0P!Z?tT&E-ll(pwf0?=F=yOzik=@nh1Clxr9}Vij89z)ePDSCYAqw?lVI?v?+&*zH z)p$CScFI8rrwId~`}9YWPFu0cW1Sf@vRELs&cbntRU6QfPK-SO*mqu|u~}8AJ!Q$z znzu}50O=YbjwKCuSVBs6&CZR#0FTu)3{}qJJYX(>QPr4$RqWiwX3NT~;>cLn*_&1H zaKpIW)JVJ>b{uo2oq>oQt3y=zJjb%fU@wLqM{SyaC6x2snMx-}ivfU<1- znu1Lh;i$3Tf$Kh5Uk))G!D1UhE8pvx&nO~w^fG)BC&L!_hQk%^p`Kp@F{cz>80W&T ziOK=Sq3fdRu*V0=S53rcIfWFazI}Twj63CG(jOB;$*b`*#B9uEnBM`hDk*EwSRdwP8?5T?xGUKs=5N83XsR*)a4|ijz|c{4tIU+4j^A5C<#5 z*$c_d=5ml~%pGxw#?*q9N7aRwPux5EyqHVkdJO=5J>84!X6P>DS8PTTz>7C#FO?k#edkntG+fJk8ZMn?pmJSO@`x-QHq;7^h6GEXLXo1TCNhH z8ZDH{*NLAjo3WM`xeb=X{((uv3H(8&r8fJJg_uSs_%hOH%JDD?hu*2NvWGYD+j)&` zz#_1%O1wF^o5ryt?O0n;`lHbzp0wQ?rcbW(F1+h7_EZZ9{>rePvLAPVZ_R|n@;b$;UchU=0j<6k8G9QuQf@76oiE*4 zXOLQ&n3$NR#p4<5NJMVC*S);5x2)eRbaAM%VxWu9ohlT;pGEk7;002enCbQ>2r-us z3#bpXP9g|mE`65VrN`+3mC)M(eMj~~eOf)do<@l+fMiTR)XO}422*1SL{wyY(%oMpBgJagtiDf zz>O6(m;};>Hi=t8o{DVC@YigqS(Qh+ix3Rwa9aliH}a}IlOCW1@?%h_bRbq-W{KHF z%Vo?-j@{Xi@=~Lz5uZP27==UGE15|g^0gzD|3x)SCEXrx`*MP^FDLl%pOi~~Il;dc z^hrwp9sYeT7iZ)-ajKy@{a`kr0-5*_!XfBpXwEcFGJ;%kV$0Nx;apKrur zJN2J~CAv{Zjj%FolyurtW8RaFmpn&zKJWL>(0;;+q(%(Hx!GMW4AcfP0YJ*Vz!F4g z!ZhMyj$BdXL@MlF%KeInmPCt~9&A!;cRw)W!Hi@0DY(GD_f?jeV{=s=cJ6e}JktJw zQORnxxj3mBxfrH=x{`_^Z1ddDh}L#V7i}$njUFRVwOX?qOTKjfPMBO4y(WiU<)epb zvB9L=%jW#*SL|Nd_G?E*_h1^M-$PG6Pc_&QqF0O-FIOpa4)PAEPsyvB)GKasmBoEt z?_Q2~QCYGH+hW31x-B=@5_AN870vY#KB~3a*&{I=f);3Kv7q4Q7s)0)gVYx2#Iz9g(F2;=+Iy4 z6KI^8GJ6D@%tpS^8boU}zpi=+(5GfIR)35PzrbuXeL1Y1N%JK7PG|^2k3qIqHfX;G zQ}~JZ-UWx|60P5?d1e;AHx!_;#PG%d=^X(AR%i`l0jSpYOpXoKFW~7ip7|xvN;2^? zsYC9fanpO7rO=V7+KXqVc;Q5z%Bj})xHVrgoR04sA2 zl~DAwv=!(()DvH*=lyhIlU^hBkA0$e*7&fJpB0|oB7)rqGK#5##2T`@_I^|O2x4GO z;xh6ROcV<9>?e0)MI(y++$-ksV;G;Xe`lh76T#Htuia+(UrIXrf9?

L(tZ$0BqX1>24?V$S+&kLZ`AodQ4_)P#Q3*4xg8}lMV-FLwC*cN$< zt65Rf%7z41u^i=P*qO8>JqXPrinQFapR7qHAtp~&RZ85$>ob|Js;GS^y;S{XnGiBc zGa4IGvDl?x%gY`vNhv8wgZnP#UYI-w*^4YCZnxkF85@ldepk$&$#3EAhrJY0U)lR{F6sM3SONV^+$;Zx8BD&Eku3K zKNLZyBni3)pGzU0;n(X@1fX8wYGKYMpLmCu{N5-}epPDxClPFK#A@02WM3!myN%bkF z|GJ4GZ}3sL{3{qXemy+#Uk{4>Kf8v11;f8I&c76+B&AQ8udd<8gU7+BeWC`akUU~U zgXoxie>MS@rBoyY8O8Tc&8id!w+_ooxcr!1?#rc$-|SBBtH6S?)1e#P#S?jFZ8u-Bs&k`yLqW|{j+%c#A4AQ>+tj$Y z^CZajspu$F%73E68Lw5q7IVREED9r1Ijsg#@DzH>wKseye>hjsk^{n0g?3+gs@7`i zHx+-!sjLx^fS;fY!ERBU+Q zVJ!e0hJH%P)z!y%1^ZyG0>PN@5W~SV%f>}c?$H8r;Sy-ui>aruVTY=bHe}$e zi&Q4&XK!qT7-XjCrDaufT@>ieQ&4G(SShUob0Q>Gznep9fR783jGuUynAqc6$pYX; z7*O@@JW>O6lKIk0G00xsm|=*UVTQBB`u1f=6wGAj%nHK_;Aqmfa!eAykDmi-@u%6~ z;*c!pS1@V8r@IX9j&rW&d*}wpNs96O2Ute>%yt{yv>k!6zfT6pru{F1M3P z2WN1JDYqoTB#(`kE{H676QOoX`cnqHl1Yaru)>8Ky~VU{)r#{&s86Vz5X)v15ULHA zAZDb{99+s~qI6;-dQ5DBjHJP@GYTwn;Dv&9kE<0R!d z8tf1oq$kO`_sV(NHOSbMwr=To4r^X$`sBW4$gWUov|WY?xccQJN}1DOL|GEaD_!@& z15p?Pj+>7d`@LvNIu9*^hPN)pwcv|akvYYq)ks%`G>!+!pW{-iXPZsRp8 z35LR;DhseQKWYSD`%gO&k$Dj6_6q#vjWA}rZcWtQr=Xn*)kJ9kacA=esi*I<)1>w^ zO_+E>QvjP)qiSZg9M|GNeLtO2D7xT6vsj`88sd!94j^AqxFLi}@w9!Y*?nwWARE0P znuI_7A-saQ+%?MFA$gttMV-NAR^#tjl_e{R$N8t2NbOlX373>e7Ox=l=;y#;M7asp zRCz*CLnrm$esvSb5{T<$6CjY zmZ(i{Rs_<#pWW>(HPaaYj`%YqBra=Ey3R21O7vUbzOkJJO?V`4-D*u4$Me0Bx$K(lYo`JO}gnC zx`V}a7m-hLU9Xvb@K2ymioF)vj12<*^oAqRuG_4u%(ah?+go%$kOpfb`T96P+L$4> zQ#S+sA%VbH&mD1k5Ak7^^dZoC>`1L%i>ZXmooA!%GI)b+$D&ziKrb)a=-ds9xk#~& z7)3iem6I|r5+ZrTRe_W861x8JpD`DDIYZNm{$baw+$)X^Jtjnl0xlBgdnNY}x%5za zkQ8E6T<^$sKBPtL4(1zi_Rd(tVth*3Xs!ulflX+70?gb&jRTnI8l+*Aj9{|d%qLZ+ z>~V9Z;)`8-lds*Zgs~z1?Fg?Po7|FDl(Ce<*c^2=lFQ~ahwh6rqSjtM5+$GT>3WZW zj;u~w9xwAhOc<kF}~`CJ68 z?(S5vNJa;kriPlim33{N5`C{9?NWhzsna_~^|K2k4xz1`xcui*LXL-1#Y}Hi9`Oo!zQ>x-kgAX4LrPz63uZ+?uG*84@PKq-KgQlMNRwz=6Yes) zY}>YN+qP}nwr$(CZQFjUOI=-6J$2^XGvC~EZ+vrqWaOXB$k?%Suf5k=4>AveC1aJ! ziaW4IS%F$_Babi)kA8Y&u4F7E%99OPtm=vzw$$ zEz#9rvn`Iot_z-r3MtV>k)YvErZ<^Oa${`2>MYYODSr6?QZu+be-~MBjwPGdMvGd!b!elsdi4% z`37W*8+OGulab8YM?`KjJ8e+jM(tqLKSS@=jimq3)Ea2EB%88L8CaM+aG7;27b?5` z4zuUWBr)f)k2o&xg{iZ$IQkJ+SK>lpq4GEacu~eOW4yNFLU!Kgc{w4&D$4ecm0f}~ zTTzquRW@`f0}|IILl`!1P+;69g^upiPA6F{)U8)muWHzexRenBU$E^9X-uIY2%&1w z_=#5*(nmxJ9zF%styBwivi)?#KMG96-H@hD-H_&EZiRNsfk7mjBq{L%!E;Sqn!mVX*}kXhwH6eh;b42eD!*~upVG@ z#smUqz$ICm!Y8wY53gJeS|Iuard0=;k5i5Z_hSIs6tr)R4n*r*rE`>38Pw&lkv{_r!jNN=;#?WbMj|l>cU(9trCq; z%nN~r^y7!kH^GPOf3R}?dDhO=v^3BeP5hF|%4GNQYBSwz;x({21i4OQY->1G=KFyu z&6d`f2tT9Yl_Z8YACZaJ#v#-(gcyeqXMhYGXb=t>)M@fFa8tHp2x;ODX=Ap@a5I=U z0G80^$N0G4=U(>W%mrrThl0DjyQ-_I>+1Tdd_AuB3qpYAqY54upwa3}owa|x5iQ^1 zEf|iTZxKNGRpI>34EwkIQ2zHDEZ=(J@lRaOH>F|2Z%V_t56Km$PUYu^xA5#5Uj4I4RGqHD56xT%H{+P8Ag>e_3pN$4m8n>i%OyJFPNWaEnJ4McUZPa1QmOh?t8~n& z&RulPCors8wUaqMHECG=IhB(-tU2XvHP6#NrLVyKG%Ee*mQ5Ps%wW?mcnriTVRc4J`2YVM>$ixSF2Xi+Wn(RUZnV?mJ?GRdw%lhZ+t&3s7g!~g{%m&i<6 z5{ib-<==DYG93I(yhyv4jp*y3#*WNuDUf6`vTM%c&hiayf(%=x@4$kJ!W4MtYcE#1 zHM?3xw63;L%x3drtd?jot!8u3qeqctceX3m;tWetK+>~q7Be$h>n6riK(5@ujLgRS zvOym)k+VAtyV^mF)$29Y`nw&ijdg~jYpkx%*^ z8dz`C*g=I?;clyi5|!27e2AuSa$&%UyR(J3W!A=ZgHF9OuKA34I-1U~pyD!KuRkjA zbkN!?MfQOeN>DUPBxoy5IX}@vw`EEB->q!)8fRl_mqUVuRu|C@KD-;yl=yKc=ZT0% zB$fMwcC|HE*0f8+PVlWHi>M`zfsA(NQFET?LrM^pPcw`cK+Mo0%8*x8@65=CS_^$cG{GZQ#xv($7J z??R$P)nPLodI;P!IC3eEYEHh7TV@opr#*)6A-;EU2XuogHvC;;k1aI8asq7ovoP!* z?x%UoPrZjj<&&aWpsbr>J$Er-7!E(BmOyEv!-mbGQGeJm-U2J>74>o5x`1l;)+P&~ z>}f^=Rx(ZQ2bm+YE0u=ZYrAV@apyt=v1wb?R@`i_g64YyAwcOUl=C!i>=Lzb$`tjv zOO-P#A+)t-JbbotGMT}arNhJmmGl-lyUpMn=2UacVZxmiG!s!6H39@~&uVokS zG=5qWhfW-WOI9g4!R$n7!|ViL!|v3G?GN6HR0Pt_L5*>D#FEj5wM1DScz4Jv@Sxnl zB@MPPmdI{(2D?;*wd>3#tjAirmUnQoZrVv`xM3hARuJksF(Q)wd4P$88fGYOT1p6U z`AHSN!`St}}UMBT9o7i|G`r$ zrB=s$qV3d6$W9@?L!pl0lf%)xs%1ko^=QY$ty-57=55PvP(^6E7cc zGJ*>m2=;fOj?F~yBf@K@9qwX0hA803Xw+b0m}+#a(>RyR8}*Y<4b+kpp|OS+!whP( zH`v{%s>jsQI9rd$*vm)EkwOm#W_-rLTHcZRek)>AtF+~<(did)*oR1|&~1|e36d-d zgtm5cv1O0oqgWC%Et@P4Vhm}Ndl(Y#C^MD03g#PH-TFy+7!Osv1z^UWS9@%JhswEq~6kSr2DITo59+; ze=ZC}i2Q?CJ~Iyu?vn|=9iKV>4j8KbxhE4&!@SQ^dVa-gK@YfS9xT(0kpW*EDjYUkoj! zE49{7H&E}k%5(>sM4uGY)Q*&3>{aitqdNnRJkbOmD5Mp5rv-hxzOn80QsG=HJ_atI-EaP69cacR)Uvh{G5dTpYG7d zbtmRMq@Sexey)||UpnZ?;g_KMZq4IDCy5}@u!5&B^-=6yyY{}e4Hh3ee!ZWtL*s?G zxG(A!<9o!CL+q?u_utltPMk+hn?N2@?}xU0KlYg?Jco{Yf@|mSGC<(Zj^yHCvhmyx z?OxOYoxbptDK()tsJ42VzXdINAMWL$0Gcw?G(g8TMB)Khw_|v9`_ql#pRd2i*?CZl z7k1b!jQB=9-V@h%;Cnl7EKi;Y^&NhU0mWEcj8B|3L30Ku#-9389Q+(Yet0r$F=+3p z6AKOMAIi|OHyzlHZtOm73}|ntKtFaXF2Fy|M!gOh^L4^62kGUoWS1i{9gsds_GWBc zLw|TaLP64z3z9?=R2|T6Xh2W4_F*$cq>MtXMOy&=IPIJ`;!Tw?PqvI2b*U1)25^<2 zU_ZPoxg_V0tngA0J+mm?3;OYw{i2Zb4x}NedZug!>EoN3DC{1i)Z{Z4m*(y{ov2%- zk(w>+scOO}MN!exSc`TN)!B=NUX`zThWO~M*ohqq;J2hx9h9}|s#?@eR!=F{QTrq~ zTcY|>azkCe$|Q0XFUdpFT=lTcyW##i;-e{}ORB4D?t@SfqGo_cS z->?^rh$<&n9DL!CF+h?LMZRi)qju!meugvxX*&jfD!^1XB3?E?HnwHP8$;uX{Rvp# zh|)hM>XDv$ZGg=$1{+_bA~u-vXqlw6NH=nkpyWE0u}LQjF-3NhATL@9rRxMnpO%f7 z)EhZf{PF|mKIMFxnC?*78(}{Y)}iztV12}_OXffJ;ta!fcFIVjdchyHxH=t%ci`Xd zX2AUB?%?poD6Zv*&BA!6c5S#|xn~DK01#XvjT!w!;&`lDXSJT4_j$}!qSPrb37vc{ z9^NfC%QvPu@vlxaZ;mIbn-VHA6miwi8qJ~V;pTZkKqqOii<1Cs}0i?uUIss;hM4dKq^1O35y?Yp=l4i zf{M!@QHH~rJ&X~8uATV><23zZUbs-J^3}$IvV_ANLS08>k`Td7aU_S1sLsfi*C-m1 z-e#S%UGs4E!;CeBT@9}aaI)qR-6NU@kvS#0r`g&UWg?fC7|b^_HyCE!8}nyh^~o@< zpm7PDFs9yxp+byMS(JWm$NeL?DNrMCNE!I^ko-*csB+dsf4GAq{=6sfyf4wb>?v1v zmb`F*bN1KUx-`ra1+TJ37bXNP%`-Fd`vVQFTwWpX@;s(%nDQa#oWhgk#mYlY*!d>( zE&!|ySF!mIyfING+#%RDY3IBH_fW$}6~1%!G`suHub1kP@&DoAd5~7J55;5_noPI6eLf{t;@9Kf<{aO0`1WNKd?<)C-|?C?)3s z>wEq@8=I$Wc~Mt$o;g++5qR+(6wt9GI~pyrDJ%c?gPZe)owvy^J2S=+M^ z&WhIE`g;;J^xQLVeCtf7b%Dg#Z2gq9hp_%g)-%_`y*zb; zn9`f`mUPN-Ts&fFo(aNTsXPA|J!TJ{0hZp0^;MYHLOcD=r_~~^ymS8KLCSeU3;^QzJNqS z5{5rEAv#l(X?bvwxpU;2%pQftF`YFgrD1jt2^~Mt^~G>T*}A$yZc@(k9orlCGv&|1 zWWvVgiJsCAtamuAYT~nzs?TQFt<1LSEx!@e0~@yd6$b5!Zm(FpBl;(Cn>2vF?k zOm#TTjFwd2D-CyA!mqR^?#Uwm{NBemP>(pHmM}9;;8`c&+_o3#E5m)JzfwN?(f-a4 zyd%xZc^oQx3XT?vcCqCX&Qrk~nu;fxs@JUoyVoi5fqpi&bUhQ2y!Ok2pzsFR(M(|U zw3E+kH_zmTRQ9dUMZWRE%Zakiwc+lgv7Z%|YO9YxAy`y28`Aw;WU6HXBgU7fl@dnt z-fFBV)}H-gqP!1;V@Je$WcbYre|dRdp{xt!7sL3Eoa%IA`5CAA%;Wq8PktwPdULo! z8!sB}Qt8#jH9Sh}QiUtEPZ6H0b*7qEKGJ%ITZ|vH)5Q^2m<7o3#Z>AKc%z7_u`rXA zqrCy{-{8;9>dfllLu$^M5L z-hXs))h*qz%~ActwkIA(qOVBZl2v4lwbM>9l70Y`+T*elINFqt#>OaVWoja8RMsep z6Or3f=oBnA3vDbn*+HNZP?8LsH2MY)x%c13@(XfuGR}R?Nu<|07{$+Lc3$Uv^I!MQ z>6qWgd-=aG2Y^24g4{Bw9ueOR)(9h`scImD=86dD+MnSN4$6 z^U*o_mE-6Rk~Dp!ANp#5RE9n*LG(Vg`1)g6!(XtDzsov$Dvz|Gv1WU68J$CkshQhS zCrc|cdkW~UK}5NeaWj^F4MSgFM+@fJd{|LLM)}_O<{rj z+?*Lm?owq?IzC%U%9EBga~h-cJbIu=#C}XuWN>OLrc%M@Gu~kFEYUi4EC6l#PR2JS zQUkGKrrS#6H7}2l0F@S11DP`@pih0WRkRJl#F;u{c&ZC{^$Z+_*lB)r)-bPgRFE;* zl)@hK4`tEP=P=il02x7-C7p%l=B`vkYjw?YhdJU9!P!jcmY$OtC^12w?vy3<<=tlY zUwHJ_0lgWN9vf>1%WACBD{UT)1qHQSE2%z|JHvP{#INr13jM}oYv_5#xsnv9`)UAO zuwgyV4YZ;O)eSc3(mka6=aRohi!HH@I#xq7kng?Acdg7S4vDJb6cI5fw?2z%3yR+| zU5v@Hm}vy;${cBp&@D=HQ9j7NcFaOYL zj-wV=eYF{|XTkFNM2uz&T8uH~;)^Zo!=KP)EVyH6s9l1~4m}N%XzPpduPg|h-&lL` zAXspR0YMOKd2yO)eMFFJ4?sQ&!`dF&!|niH*!^*Ml##o0M(0*uK9&yzekFi$+mP9s z>W9d%Jb)PtVi&-Ha!o~Iyh@KRuKpQ@)I~L*d`{O8!kRObjO7=n+Gp36fe!66neh+7 zW*l^0tTKjLLzr`x4`_8&on?mjW-PzheTNox8Hg7Nt@*SbE-%kP2hWYmHu#Fn@Q^J(SsPUz*|EgOoZ6byg3ew88UGdZ>9B2Tq=jF72ZaR=4u%1A6Vm{O#?@dD!(#tmR;eP(Fu z{$0O%=Vmua7=Gjr8nY%>ul?w=FJ76O2js&17W_iq2*tb!i{pt#`qZB#im9Rl>?t?0c zicIC}et_4d+CpVPx)i4~$u6N-QX3H77ez z?ZdvXifFk|*F8~L(W$OWM~r`pSk5}#F?j_5u$Obu9lDWIknO^AGu+Blk7!9Sb;NjS zncZA?qtASdNtzQ>z7N871IsPAk^CC?iIL}+{K|F@BuG2>qQ;_RUYV#>hHO(HUPpk@ z(bn~4|F_jiZi}Sad;_7`#4}EmD<1EiIxa48QjUuR?rC}^HRocq`OQPM@aHVKP9E#q zy%6bmHygCpIddPjE}q_DPC`VH_2m;Eey&ZH)E6xGeStOK7H)#+9y!%-Hm|QF6w#A( zIC0Yw%9j$s-#odxG~C*^MZ?M<+&WJ+@?B_QPUyTg9DJGtQN#NIC&-XddRsf3n^AL6 zT@P|H;PvN;ZpL0iv$bRb7|J{0o!Hq+S>_NrH4@coZtBJu#g8#CbR7|#?6uxi8d+$g z87apN>EciJZ`%Zv2**_uiET9Vk{pny&My;+WfGDw4EVL#B!Wiw&M|A8f1A@ z(yFQS6jfbH{b8Z-S7D2?Ixl`j0{+ZnpT=;KzVMLW{B$`N?Gw^Fl0H6lT61%T2AU**!sX0u?|I(yoy&Xveg7XBL&+>n6jd1##6d>TxE*Vj=8lWiG$4=u{1UbAa5QD>5_ z;Te^42v7K6Mmu4IWT6Rnm>oxrl~b<~^e3vbj-GCdHLIB_>59}Ya+~OF68NiH=?}2o zP(X7EN=quQn&)fK>M&kqF|<_*H`}c zk=+x)GU>{Af#vx&s?`UKUsz})g^Pc&?Ka@t5$n$bqf6{r1>#mWx6Ep>9|A}VmWRnowVo`OyCr^fHsf# zQjQ3Ttp7y#iQY8l`zEUW)(@gGQdt(~rkxlkefskT(t%@i8=|p1Y9Dc5bc+z#n$s13 zGJk|V0+&Ekh(F};PJzQKKo+FG@KV8a<$gmNSD;7rd_nRdc%?9)p!|B-@P~kxQG}~B zi|{0}@}zKC(rlFUYp*dO1RuvPC^DQOkX4<+EwvBAC{IZQdYxoq1Za!MW7%p7gGr=j zzWnAq%)^O2$eItftC#TTSArUyL$U54-O7e|)4_7%Q^2tZ^0-d&3J1}qCzR4dWX!)4 zzIEKjgnYgMus^>6uw4Jm8ga6>GBtMjpNRJ6CP~W=37~||gMo_p@GA@#-3)+cVYnU> zE5=Y4kzl+EbEh%dhQokB{gqNDqx%5*qBusWV%!iprn$S!;oN_6E3?0+umADVs4ako z?P+t?m?};gev9JXQ#Q&KBpzkHPde_CGu-y z<{}RRAx=xlv#mVi+Ibrgx~ujW$h{?zPfhz)Kp7kmYS&_|97b&H&1;J-mzrBWAvY} zh8-I8hl_RK2+nnf&}!W0P+>5?#?7>npshe<1~&l_xqKd0_>dl_^RMRq@-Myz&|TKZBj1=Q()) zF{dBjv5)h=&Z)Aevx}+i|7=R9rG^Di!sa)sZCl&ctX4&LScQ-kMncgO(9o6W6)yd< z@Rk!vkja*X_N3H=BavGoR0@u0<}m-7|2v!0+2h~S2Q&a=lTH91OJsvms2MT~ zY=c@LO5i`mLpBd(vh|)I&^A3TQLtr>w=zoyzTd=^f@TPu&+*2MtqE$Avf>l>}V|3-8Fp2hzo3y<)hr_|NO(&oSD z!vEjTWBxbKTiShVl-U{n*B3#)3a8$`{~Pk}J@elZ=>Pqp|MQ}jrGv7KrNcjW%TN_< zZz8kG{#}XoeWf7qY?D)L)8?Q-b@Na&>i=)(@uNo zr;cH98T3$Iau8Hn*@vXi{A@YehxDE2zX~o+RY`)6-X{8~hMpc#C`|8y> zU8Mnv5A0dNCf{Ims*|l-^ z(MRp{qoGohB34|ggDI*p!Aw|MFyJ|v+<+E3brfrI)|+l3W~CQLPbnF@G0)P~Ly!1TJLp}xh8uW`Q+RB-v`MRYZ9Gam3cM%{ zb4Cb*f)0deR~wtNb*8w-LlIF>kc7DAv>T0D(a3@l`k4TFnrO+g9XH7;nYOHxjc4lq zMmaW6qpgAgy)MckYMhl?>sq;-1E)-1llUneeA!ya9KM$)DaNGu57Z5aE>=VST$#vb zFo=uRHr$0M{-ha>h(D_boS4zId;3B|Tpqo|?B?Z@I?G(?&Iei+-{9L_A9=h=Qfn-U z1wIUnQe9!z%_j$F_{rf&`ZFSott09gY~qrf@g3O=Y>vzAnXCyL!@(BqWa)Zqt!#_k zfZHuwS52|&&)aK;CHq9V-t9qt0au{$#6c*R#e5n3rje0hic7c7m{kW$p(_`wB=Gw7 z4k`1Hi;Mc@yA7dp@r~?@rfw)TkjAW++|pkfOG}0N|2guek}j8Zen(!+@7?qt_7ndX zB=BG6WJ31#F3#Vk3=aQr8T)3`{=p9nBHlKzE0I@v`{vJ}h8pd6vby&VgFhzH|q;=aonunAXL6G2y(X^CtAhWr*jI zGjpY@raZDQkg*aMq}Ni6cRF z{oWv}5`nhSAv>usX}m^GHt`f(t8@zHc?K|y5Zi=4G*UG1Sza{$Dpj%X8 zzEXaKT5N6F5j4J|w#qlZP!zS7BT)9b+!ZSJdToqJts1c!)fwih4d31vfb{}W)EgcA zH2pZ^8_k$9+WD2n`6q5XbOy8>3pcYH9 z07eUB+p}YD@AH!}p!iKv><2QF-Y^&xx^PAc1F13A{nUeCDg&{hnix#FiO!fe(^&%Qcux!h znu*S!s$&nnkeotYsDthh1dq(iQrE|#f_=xVgfiiL&-5eAcC-> z5L0l|DVEM$#ulf{bj+Y~7iD)j<~O8CYM8GW)dQGq)!mck)FqoL^X zwNdZb3->hFrbHFm?hLvut-*uK?zXn3q1z|UX{RZ;-WiLoOjnle!xs+W0-8D)kjU#R z+S|A^HkRg$Ij%N4v~k`jyHffKaC~=wg=9)V5h=|kLQ@;^W!o2^K+xG&2n`XCd>OY5Ydi= zgHH=lgy++erK8&+YeTl7VNyVm9-GfONlSlVb3)V9NW5tT!cJ8d7X)!b-$fb!s76{t z@d=Vg-5K_sqHA@Zx-L_}wVnc@L@GL9_K~Zl(h5@AR#FAiKad8~KeWCo@mgXIQ#~u{ zgYFwNz}2b6Vu@CP0XoqJ+dm8px(5W5-Jpis97F`+KM)TuP*X8H@zwiVKDKGVp59pI zifNHZr|B+PG|7|Y<*tqap0CvG7tbR1R>jn70t1X`XJixiMVcHf%Ez*=xm1(CrTSDt z0cle!+{8*Ja&EOZ4@$qhBuKQ$U95Q%rc7tg$VRhk?3=pE&n+T3upZg^ZJc9~c2es% zh7>+|mrmA-p&v}|OtxqmHIBgUxL~^0+cpfkSK2mhh+4b=^F1Xgd2)}U*Yp+H?ls#z zrLxWg_hm}AfK2XYWr!rzW4g;+^^&bW%LmbtRai9f3PjU${r@n`JThy-cphbcwn)rq9{A$Ht`lmYKxOacy z6v2R(?gHhD5@&kB-Eg?4!hAoD7~(h>(R!s1c1Hx#s9vGPePUR|of32bS`J5U5w{F) z>0<^ktO2UHg<0{oxkdOQ;}coZDQph8p6ruj*_?uqURCMTac;>T#v+l1Tc~%^k-Vd@ zkc5y35jVNc49vZpZx;gG$h{%yslDI%Lqga1&&;mN{Ush1c7p>7e-(zp}6E7f-XmJb4nhk zb8zS+{IVbL$QVF8pf8}~kQ|dHJAEATmmnrb_wLG}-yHe>W|A&Y|;muy-d^t^<&)g5SJfaTH@P1%euONny=mxo+C z4N&w#biWY41r8k~468tvuYVh&XN&d#%QtIf9;iVXfWY)#j=l`&B~lqDT@28+Y!0E+MkfC}}H*#(WKKdJJq=O$vNYCb(ZG@p{fJgu;h z21oHQ(14?LeT>n5)s;uD@5&ohU!@wX8w*lB6i@GEH0pM>YTG+RAIWZD;4#F1&F%Jp zXZUml2sH0!lYJT?&sA!qwez6cXzJEd(1ZC~kT5kZSp7(@=H2$Azb_*W&6aA|9iwCL zdX7Q=42;@dspHDwYE?miGX#L^3xD&%BI&fN9^;`v4OjQXPBaBmOF1;#C)8XA(WFlH zycro;DS2?(G&6wkr6rqC>rqDv3nfGw3hmN_9Al>TgvmGsL8_hXx09};l9Ow@)F5@y z#VH5WigLDwZE4nh^7&@g{1FV^UZ%_LJ-s<{HN*2R$OPg@R~Z`c-ET*2}XB@9xvAjrK&hS=f|R8Gr9 zr|0TGOsI7RD+4+2{ZiwdVD@2zmg~g@^D--YL;6UYGSM8i$NbQr4!c7T9rg!8;TM0E zT#@?&S=t>GQm)*ua|?TLT2ktj#`|R<_*FAkOu2Pz$wEc%-=Y9V*$&dg+wIei3b*O8 z2|m$!jJG!J!ZGbbIa!(Af~oSyZV+~M1qGvelMzPNE_%5?c2>;MeeG2^N?JDKjFYCy z7SbPWH-$cWF9~fX%9~v99L!G(wi!PFp>rB!9xj7=Cv|F+7CsGNwY0Q_J%FID%C^CBZQfJ9K(HK%k31j~e#&?hQ zNuD6gRkVckU)v+53-fc} z7ZCzYN-5RG4H7;>>Hg?LU9&5_aua?A0)0dpew1#MMlu)LHe(M;OHjHIUl7|%%)YPo z0cBk;AOY00%Fe6heoN*$(b<)Cd#^8Iu;-2v@>cE-OB$icUF9EEoaC&q8z9}jMTT2I z8`9;jT%z0;dy4!8U;GW{i`)3!c6&oWY`J3669C!tM<5nQFFrFRglU8f)5Op$GtR-3 zn!+SPCw|04sv?%YZ(a7#L?vsdr7ss@WKAw&A*}-1S|9~cL%uA+E~>N6QklFE>8W|% zyX-qAUGTY1hQ-+um`2|&ji0cY*(qN!zp{YpDO-r>jPk*yuVSay<)cUt`t@&FPF_&$ zcHwu1(SQ`I-l8~vYyUxm@D1UEdFJ$f5Sw^HPH7b!9 zzYT3gKMF((N(v0#4f_jPfVZ=ApN^jQJe-X$`A?X+vWjLn_%31KXE*}5_}d8 zw_B1+a#6T1?>M{ronLbHIlEsMf93muJ7AH5h%;i99<~JX^;EAgEB1uHralD*!aJ@F zV2ruuFe9i2Q1C?^^kmVy921eb=tLDD43@-AgL^rQ3IO9%+vi_&R2^dpr}x{bCVPej z7G0-0o64uyWNtr*loIvslyo0%)KSDDKjfThe0hcqs)(C-MH1>bNGBDRTW~scy_{w} zp^aq8Qb!h9Lwielq%C1b8=?Z=&U)ST&PHbS)8Xzjh2DF?d{iAv)Eh)wsUnf>UtXN( zL7=$%YrZ#|^c{MYmhn!zV#t*(jdmYdCpwqpZ{v&L8KIuKn`@IIZfp!uo}c;7J57N` zAxyZ-uA4=Gzl~Ovycz%MW9ZL7N+nRo&1cfNn9(1H5eM;V_4Z_qVann7F>5f>%{rf= zPBZFaV@_Sobl?Fy&KXyzFDV*FIdhS5`Uc~S^Gjo)aiTHgn#<0C=9o-a-}@}xDor;D zZyZ|fvf;+=3MZd>SR1F^F`RJEZo+|MdyJYQAEauKu%WDol~ayrGU3zzbHKsnHKZ*z zFiwUkL@DZ>!*x05ql&EBq@_Vqv83&?@~q5?lVmffQZ+V-=qL+!u4Xs2Z2zdCQ3U7B&QR9_Iggy} z(om{Y9eU;IPe`+p1ifLx-XWh?wI)xU9ik+m#g&pGdB5Bi<`PR*?92lE0+TkRuXI)z z5LP!N2+tTc%cB6B1F-!fj#}>S!vnpgVU~3!*U1ej^)vjUH4s-bd^%B=ItQqDCGbrEzNQi(dJ`J}-U=2{7-d zK8k^Rlq2N#0G?9&1?HSle2vlkj^KWSBYTwx`2?9TU_DX#J+f+qLiZCqY1TXHFxXZqYMuD@RU$TgcnCC{_(vwZ-*uX)~go#%PK z@}2Km_5aQ~(<3cXeJN6|F8X_1@L%@xTzs}$_*E|a^_URF_qcF;Pfhoe?FTFwvjm1o z8onf@OY@jC2tVcMaZS;|T!Ks(wOgPpRzRnFS-^RZ4E!9dsnj9sFt609a|jJbb1Dt@ z<=Gal2jDEupxUSwWu6zp<<&RnAA;d&4gKVG0iu6g(DsST(4)z6R)zDpfaQ}v{5ARt zyhwvMtF%b-YazR5XLz+oh=mn;y-Mf2a8>7?2v8qX;19y?b>Z5laGHvzH;Nu9S`B8} zI)qN$GbXIQ1VL3lnof^6TS~rvPVg4V?Dl2Bb*K2z4E{5vy<(@@K_cN@U>R!>aUIRnb zL*)=787*cs#zb31zBC49x$`=fkQbMAef)L2$dR{)6BAz!t5U_B#1zZG`^neKSS22oJ#5B=gl%U=WeqL9REF2g zZnfCb0?quf?Ztj$VXvDSWoK`0L=Zxem2q}!XWLoT-kYMOx)!7fcgT35uC~0pySEme z`{wGWTkGr7>+Kb^n;W?BZH6ZP(9tQX%-7zF>vc2}LuWDI(9kh1G#7B99r4x6;_-V+k&c{nPUrR zAXJGRiMe~aup{0qzmLNjS_BC4cB#sXjckx{%_c&^xy{M61xEb>KW_AG5VFXUOjAG4 z^>Qlm9A#1N{4snY=(AmWzatb!ngqiqPbBZ7>Uhb3)dTkSGcL#&SH>iMO-IJBPua`u zo)LWZ>=NZLr758j{%(|uQuZ)pXq_4c!!>s|aDM9#`~1bzK3J1^^D#<2bNCccH7~-X}Ggi!pIIF>uFx%aPARGQsnC8ZQc8lrQ5o~smqOg>Ti^GNme94*w z)JZy{_{#$jxGQ&`M z!OMvZMHR>8*^>eS%o*6hJwn!l8VOOjZQJvh)@tnHVW&*GYPuxqXw}%M!(f-SQf`=L z5;=5w2;%82VMH6Xi&-K3W)o&K^+vJCepWZ-rW%+Dc6X3(){z$@4zjYxQ|}8UIojeC zYZpQ1dU{fy=oTr<4VX?$q)LP}IUmpiez^O&N3E_qPpchGTi5ZM6-2ScWlQq%V&R2Euz zO|Q0Hx>lY1Q1cW5xHv5!0OGU~PVEqSuy#fD72d#O`N!C;o=m+YioGu-wH2k6!t<~K zSr`E=W9)!g==~x9VV~-8{4ZN9{~-A9zJpRe%NGg$+MDuI-dH|b@BD)~>pPCGUNNzY zMDg||0@XGQgw`YCt5C&A{_+J}mvV9Wg{6V%2n#YSRN{AP#PY?1FF1#|vO_%e+#`|2*~wGAJaeRX6=IzFNeWhz6gJc8+(03Ph4y6ELAm=AkN7TOgMUEw*N{= z_)EIDQx5q22oUR+_b*tazu9+pX|n1c*IB-}{DqIj z-?E|ks{o3AGRNb;+iKcHkZvYJvFsW&83RAPs1Oh@IWy%l#5x2oUP6ZCtv+b|q>jsf zZ_9XO;V!>n`UxH1LvH8)L4?8raIvasEhkpQoJ`%!5rBs!0Tu(s_D{`4opB;57)pkX z4$A^8CsD3U5*!|bHIEqsn~{q+Ddj$ME@Gq4JXtgVz&7l{Ok!@?EA{B3P~NAqb9)4? zkQo30A^EbHfQ@87G5&EQTd`frrwL)&Yw?%-W@uy^Gn23%j?Y!Iea2xw<-f;esq zf%w5WN@E1}zyXtYv}}`U^B>W`>XPmdLj%4{P298|SisrE;7HvXX;A}Ffi8B#3Lr;1 zHt6zVb`8{#+e$*k?w8|O{Uh|&AG}|DG1PFo1i?Y*cQm$ZwtGcVgMwtBUDa{~L1KT-{jET4w60>{KZ27vXrHJ;fW{6| z=|Y4!&UX020wU1>1iRgB@Q#m~1^Z^9CG1LqDhYBrnx%IEdIty z!46iOoKlKs)c}newDG)rWUikD%j`)p z_w9Ph&e40=(2eBy;T!}*1p1f1SAUDP9iWy^u^Ubdj21Kn{46;GR+hwLO=4D11@c~V zI8x&(D({K~Df2E)Nx_yQvYfh4;MbMJ@Z}=Dt3_>iim~QZ*hZIlEs0mEb z_54+&*?wMD`2#vsQRN3KvoT>hWofI_Vf(^C1ff-Ike@h@saEf7g}<9T`W;HAne-Nd z>RR+&SP35w)xKn8^U$7))PsM!jKwYZ*RzEcG-OlTrX3}9a{q%#Un5E5W{{hp>w~;` zGky+3(vJvQyGwBo`tCpmo0mo((?nM8vf9aXrrY1Ve}~TuVkB(zeds^jEfI}xGBCM2 zL1|#tycSaWCurP+0MiActG3LCas@_@tao@(R1ANlwB$4K53egNE_;!&(%@Qo$>h`^1S_!hN6 z)vZtG$8fN!|BXBJ=SI>e(LAU(y(i*PHvgQ2llulxS8>qsimv7yL}0q_E5WiAz7)(f zC(ahFvG8&HN9+6^jGyLHM~$)7auppeWh_^zKk&C_MQ~8;N??OlyH~azgz5fe^>~7F zl3HnPN3z-kN)I$4@`CLCMQx3sG~V8hPS^}XDXZrQA>}mQPw%7&!sd(Pp^P=tgp-s^ zjl}1-KRPNWXgV_K^HkP__SR`S-|OF0bR-N5>I%ODj&1JUeAQ3$9i;B~$S6}*^tK?= z**%aCiH7y?xdY?{LgVP}S0HOh%0%LI$wRx;$T|~Y8R)Vdwa}kGWv8?SJVm^>r6+%I z#lj1aR94{@MP;t-scEYQWc#xFA30^}?|BeX*W#9OL;Q9#WqaaM546j5j29((^_8Nu z4uq}ESLr~r*O7E7$D{!k9W>`!SLoyA53i9QwRB{!pHe8um|aDE`Cg0O*{jmor)^t)3`>V>SWN-2VJcFmj^1?~tT=JrP`fVh*t zXHarp=8HEcR#vFe+1a%XXuK+)oFs`GDD}#Z+TJ}Ri`FvKO@ek2ayn}yaOi%(8p%2$ zpEu)v0Jym@f}U|-;}CbR=9{#<^z28PzkkTNvyKvJDZe+^VS2bES3N@Jq!-*}{oQlz z@8bgC_KnDnT4}d#&Cpr!%Yb?E!brx0!eVOw~;lLwUoz#Np%d$o%9scc3&zPm`%G((Le|6o1 zM(VhOw)!f84zG^)tZ1?Egv)d8cdNi+T${=5kV+j;Wf%2{3g@FHp^Gf*qO0q!u$=m9 zCaY`4mRqJ;FTH5`a$affE5dJrk~k`HTP_7nGTY@B9o9vvnbytaID;^b=Tzp7Q#DmD zC(XEN)Ktn39z5|G!wsVNnHi) z%^q94!lL|hF`IijA^9NR0F$@h7k5R^ljOW(;Td9grRN0Mb)l_l7##{2nPQ@?;VjXv zaLZG}yuf$r$<79rVPpXg?6iiieX|r#&`p#Con2i%S8*8F}(E) zI5E6c3tG*<;m~6>!&H!GJ6zEuhH7mkAzovdhLy;)q z{H2*8I^Pb}xC4s^6Y}6bJvMu=8>g&I)7!N!5QG$xseeU#CC?ZM-TbjsHwHgDGrsD= z{%f;@Sod+Ch66Ko2WF~;Ty)v>&x^aovCbCbD7>qF*!?BXmOV3(s|nxsb*Lx_2lpB7 zokUnzrk;P=T-&kUHO}td+Zdj!3n&NR?K~cRU zAXU!DCp?51{J4w^`cV#ye}(`SQhGQkkMu}O3M*BWt4UsC^jCFUy;wTINYmhD$AT;4 z?Xd{HaJjP`raZ39qAm;%beDbrLpbRf(mkKbANan7XsL>_pE2oo^$TgdidjRP!5-`% zv0d!|iKN$c0(T|L0C~XD0aS8t{*&#LnhE;1Kb<9&=c2B+9JeLvJr*AyyRh%@jHej=AetOMSlz^=!kxX>>B{2B1uIrQyfd8KjJ+DBy!h)~*(!|&L4^Q_07SQ~E zcemVP`{9CwFvPFu7pyVGCLhH?LhEVb2{7U+Z_>o25#+3<|8%1T^5dh}*4(kfJGry} zm%r#hU+__Z;;*4fMrX=Bkc@7|v^*B;HAl0((IBPPii%X9+u3DDF6%bI&6?Eu$8&aWVqHIM7mK6?Uvq$1|(-T|)IV<>e?!(rY zqkmO1MRaLeTR=)io(0GVtQT@s6rN%C6;nS3@eu;P#ry4q;^O@1ZKCJyp_Jo)Ty^QW z+vweTx_DLm{P-XSBj~Sl<%_b^$=}odJ!S2wAcxenmzFGX1t&Qp8Vxz2VT`uQsQYtdn&_0xVivIcxZ_hnrRtwq4cZSj1c-SG9 z7vHBCA=fd0O1<4*=lu$6pn~_pVKyL@ztw1swbZi0B?spLo56ZKu5;7ZeUml1Ws1?u zqMf1p{5myAzeX$lAi{jIUqo1g4!zWLMm9cfWcnw`k6*BR^?$2(&yW?>w;G$EmTA@a z6?y#K$C~ZT8+v{87n5Dm&H6Pb_EQ@V0IWmG9cG=O;(;5aMWWrIPzz4Q`mhK;qQp~a z+BbQrEQ+w{SeiuG-~Po5f=^EvlouB@_|4xQXH@A~KgpFHrwu%dwuCR)=B&C(y6J4J zvoGk9;lLs9%iA-IJGU#RgnZZR+@{5lYl8(e1h6&>Vc_mvg0d@);X zji4T|n#lB!>pfL|8tQYkw?U2bD`W{na&;*|znjmalA&f;*U++_aBYerq;&C8Kw7mI z7tsG*?7*5j&dU)Lje;^{D_h`%(dK|pB*A*1(Jj)w^mZ9HB|vGLkF1GEFhu&rH=r=8 zMxO42e{Si6$m+Zj`_mXb&w5Q(i|Yxyg?juUrY}78uo@~3v84|8dfgbPd0iQJRdMj< zncCNGdMEcsxu#o#B5+XD{tsg*;j-eF8`mp~K8O1J!Z0+>0=7O=4M}E?)H)ENE;P*F z$Ox?ril_^p0g7xhDUf(q652l|562VFlC8^r8?lQv;TMvn+*8I}&+hIQYh2 z1}uQQaag&!-+DZ@|C+C$bN6W;S-Z@)d1|en+XGvjbOxCa-qAF*LA=6s(Jg+g;82f$ z(Vb)8I)AH@cdjGFAR5Rqd0wiNCu!xtqWbcTx&5kslzTb^7A78~Xzw1($UV6S^VWiP zFd{Rimd-0CZC_Bu(WxBFW7+k{cOW7DxBBkJdJ;VsJ4Z@lERQr%3eVv&$%)b%<~ zCl^Y4NgO}js@u{|o~KTgH}>!* z_iDNqX2(As7T0xivMH|3SC1ivm8Q}6Ffcd7owUKN5lHAtzMM4<0v+ykUT!QiowO;`@%JGv+K$bBx@*S7C8GJVqQ_K>12}M`f_Ys=S zKFh}HM9#6Izb$Y{wYzItTy+l5U2oL%boCJn?R3?jP@n$zSIwlmyGq30Cw4QBO|14` zW5c);AN*J3&eMFAk$SR~2k|&+&Bc$e>s%c{`?d~85S-UWjA>DS5+;UKZ}5oVa5O(N zqqc@>)nee)+4MUjH?FGv%hm2{IlIF-QX}ym-7ok4Z9{V+ZHVZQl$A*x!(q%<2~iVv znUa+BX35&lCb#9VE-~Y^W_f;Xhl%vgjwdjzMy$FsSIj&ok}L+X`4>J=9BkN&nu^E*gbhj3(+D>C4E z@Fwq_=N)^bKFSHTzZk?-gNU$@l}r}dwGyh_fNi=9b|n}J>&;G!lzilbWF4B}BBq4f zYIOl?b)PSh#XTPp4IS5ZR_2C!E)Z`zH0OW%4;&~z7UAyA-X|sh9@~>cQW^COA9hV4 zXcA6qUo9P{bW1_2`eo6%hgbN%(G-F1xTvq!sc?4wN6Q4`e9Hku zFwvlAcRY?6h^Fj$R8zCNEDq8`=uZB8D-xn)tA<^bFFy}4$vA}Xq0jAsv1&5!h!yRA zU()KLJya5MQ`q&LKdH#fwq&(bNFS{sKlEh_{N%{XCGO+po#(+WCLmKW6&5iOHny>g z3*VFN?mx!16V5{zyuMWDVP8U*|BGT$(%IO|)?EF|OI*sq&RovH!N%=>i_c?K*A>>k zyg1+~++zY4Q)J;VWN0axhoIKx;l&G$gvj(#go^pZskEVj8^}is3Jw26LzYYVos0HX zRPvmK$dVxM8(Tc?pHFe0Z3uq){{#OK3i-ra#@+;*=ui8)y6hsRv z4Fxx1c1+fr!VI{L3DFMwXKrfl#Q8hfP@ajgEau&QMCxd{g#!T^;ATXW)nUg&$-n25 zruy3V!!;{?OTobo|0GAxe`Acn3GV@W=&n;~&9 zQM>NWW~R@OYORkJAo+eq1!4vzmf9K%plR4(tB@TR&FSbDoRgJ8qVcH#;7lQub*nq&?Z>7WM=oeEVjkaG zT#f)=o!M2DO5hLR+op>t0CixJCIeXH*+z{-XS|%jx)y(j&}Wo|3!l7{o)HU3m7LYyhv*xF&tq z%IN7N;D4raue&&hm0xM=`qv`+TK@;_xAcGKuK(2|75~ar2Yw)geNLSmVxV@x89bQu zpViVKKnlkwjS&&c|-X6`~xdnh}Ps)Hs z4VbUL^{XNLf7_|Oi>tA%?SG5zax}esF*FH3d(JH^Gvr7Rp*n=t7frH!U;!y1gJB^i zY_M$KL_}mW&XKaDEi9K-wZR|q*L32&m+2n_8lq$xRznJ7p8}V>w+d@?uB!eS3#u<} zIaqi!b!w}a2;_BfUUhGMy#4dPx>)_>yZ`ai?Rk`}d0>~ce-PfY-b?Csd(28yX22L% zI7XI>OjIHYTk_@Xk;Gu^F52^Gn6E1&+?4MxDS2G_#PQ&yXPXP^<-p|2nLTb@AAQEY zI*UQ9Pmm{Kat}wuazpjSyXCdnrD&|C1c5DIb1TnzF}f4KIV6D)CJ!?&l&{T)e4U%3HTSYqsQ zo@zWB1o}ceQSV)<4G<)jM|@@YpL+XHuWsr5AYh^Q{K=wSV99D~4RRU52FufmMBMmd z_H}L#qe(}|I9ZyPRD6kT>Ivj&2Y?qVZq<4bG_co_DP`sE*_Xw8D;+7QR$Uq(rr+u> z8bHUWbV19i#)@@G4bCco@Xb<8u~wVDz9S`#k@ciJtlu@uP1U0X?yov8v9U3VOig2t zL9?n$P3=1U_Emi$#slR>N5wH-=J&T=EdUHA}_Z zZIl3nvMP*AZS9{cDqFanrA~S5BqxtNm9tlu;^`)3X&V4tMAkJ4gEIPl= zoV!Gyx0N{3DpD@)pv^iS*dl2FwANu;1;%EDl}JQ7MbxLMAp>)UwNwe{=V}O-5C*>F zu?Ny+F64jZn<+fKjF01}8h5H_3pey|;%bI;SFg$w8;IC<8l|3#Lz2;mNNik6sVTG3 z+Su^rIE#40C4a-587$U~%KedEEw1%r6wdvoMwpmlXH$xPnNQN#f%Z7|p)nC>WsuO= z4zyqapLS<8(UJ~Qi9d|dQijb_xhA2)v>la)<1md5s^R1N&PiuA$^k|A<+2C?OiHbj z>Bn$~t)>Y(Zb`8hW7q9xQ=s>Rv81V+UiuZJc<23HplI88isqRCId89fb`Kt|CxVIg znWcwprwXnotO>3s&Oypkte^9yJjlUVVxSe%_xlzmje|mYOVPH^vjA=?6xd0vaj0Oz zwJ4OJNiFdnHJX3rw&inskjryukl`*fRQ#SMod5J|KroJRsVXa5_$q7whSQ{gOi*s0 z1LeCy|JBWRsDPn7jCb4s(p|JZiZ8+*ExC@Vj)MF|*Vp{B(ziccSn`G1Br9bV(v!C2 z6#?eqpJBc9o@lJ#^p-`-=`4i&wFe>2)nlPK1p9yPFzJCzBQbpkcR>={YtamIw)3nt z(QEF;+)4`>8^_LU)_Q3 zC5_7lgi_6y>U%m)m@}Ku4C}=l^J=<<7c;99ec3p{aR+v=diuJR7uZi%aQv$oP?dn?@6Yu_+*^>T0ptf(oobdL;6)N-I!TO`zg^Xbv3#L0I~sn@WGk-^SmPh5>W+LB<+1PU}AKa?FCWF|qMNELOgdxR{ zbqE7@jVe+FklzdcD$!(A$&}}H*HQFTJ+AOrJYnhh}Yvta(B zQ_bW4Rr;R~&6PAKwgLWXS{Bnln(vUI+~g#kl{r+_zbngT`Y3`^Qf=!PxN4IYX#iW4 zucW7@LLJA9Zh3(rj~&SyN_pjO8H&)|(v%!BnMWySBJV=eSkB3YSTCyIeJ{i;(oc%_hk{$_l;v>nWSB)oVeg+blh=HB5JSlG_r7@P z3q;aFoZjD_qS@zygYqCn=;Zxjo!?NK!%J$ z52lOP`8G3feEj+HTp@Tnn9X~nG=;tS+z}u{mQX_J0kxtr)O30YD%oo)L@wy`jpQYM z@M>Me=95k1p*FW~rHiV1CIfVc{K8r|#Kt(ApkXKsDG$_>76UGNhHExFCw#Ky9*B-z zNq2ga*xax!HMf_|Vp-86r{;~YgQKqu7%szk8$hpvi_2I`OVbG1doP(`gn}=W<8%Gn z%81#&WjkH4GV;4u43EtSW>K_Ta3Zj!XF?;SO3V#q=<=>Tc^@?A`i;&`-cYj|;^ zEo#Jl5zSr~_V-4}y8pnufXLa80vZY4z2ko7fj>DR)#z=wWuS1$$W!L?(y}YC+yQ|G z@L&`2upy3f>~*IquAjkVNU>}c10(fq#HdbK$~Q3l6|=@-eBbo>B9(6xV`*)sae58*f zym~RRVx;xoCG3`JV`xo z!lFw)=t2Hy)e!IFs?0~7osWk(d%^wxq&>_XD4+U#y&-VF%4z?XH^i4w`TxpF{`XhZ z%G}iEzf!T(l>g;W9<~K+)$g!{UvhW{E0Lis(S^%I8OF&%kr!gJ&fMOpM=&=Aj@wuL zBX?*6i51Qb$uhkwkFYkaD_UDE+)rh1c;(&Y=B$3)J&iJfQSx!1NGgPtK!$c9OtJuu zX(pV$bfuJpRR|K(dp@^j}i&HeJOh@|7lWo8^$*o~Xqo z5Sb+!EtJ&e@6F+h&+_1ETbg7LfP5GZjvIUIN3ibCOldAv z)>YdO|NH$x7AC8dr=<2ekiY1%fN*r~e5h6Yaw<{XIErujKV~tiyrvV_DV0AzEknC- zR^xKM3i<1UkvqBj3C{wDvytOd+YtDSGu!gEMg+!&|8BQrT*|p)(dwQLEy+ zMtMzij3zo40)CA!BKZF~yWg?#lWhqD3@qR)gh~D{uZaJO;{OWV8XZ_)J@r3=)T|kt zUS1pXr6-`!Z}w2QR7nP%d?ecf90;K_7C3d!UZ`N(TZoWNN^Q~RjVhQG{Y<%E1PpV^4 z-m-K+$A~-+VDABs^Q@U*)YvhY4Znn2^w>732H?NRK(5QSS$V@D7yz2BVX4)f5A04~$WbxGOam22>t&uD)JB8-~yiQW6ik;FGblY_I>SvB_z2?PS z*Qm&qbKI{H1V@YGWzpx`!v)WeLT02};JJo*#f$a*FH?IIad-^(;9XC#YTWN6;Z6+S zm4O1KH=#V@FJw7Pha0!9Vb%ZIM$)a`VRMoiN&C|$YA3~ZC*8ayZRY^fyuP6$n%2IU z$#XceYZeqLTXw(m$_z|33I$B4k~NZO>pP6)H_}R{E$i%USGy{l{-jOE;%CloYPEU+ zRFxOn4;7lIOh!7abb23YKD+_-?O z0FP9otcAh+oSj;=f#$&*ExUHpd&e#bSF%#8*&ItcL2H$Sa)?pt0Xtf+t)z$_u^wZi z44oE}r4kIZGy3!Mc8q$B&6JqtnHZ>Znn!Zh@6rgIu|yU+zG8q`q9%B18|T|oN3zMq z`l&D;U!OL~%>vo&q0>Y==~zLiCZk4v%s_7!9DxQ~id1LLE93gf*gg&2$|hB#j8;?3 z5v4S;oM6rT{Y;I+#FdmNw z){d%tNM<<#GN%n9ox7B=3#;u7unZ~tLB_vRZ52a&2=IM)2VkXm=L+Iqq~uk#Dug|x z>S84e+A7EiOY5lj*!q?6HDkNh~0g;0Jy(al!ZHHDtur9T$y-~)94HelX1NHjXWIM7UAe}$?jiz z9?P4`I0JM=G5K{3_%2jPLC^_Mlw?-kYYgb7`qGa3@dn|^1fRMwiyM@Ch z;CB&o7&&?c5e>h`IM;Wnha0QKnEp=$hA8TJgR-07N~U5(>9vJzeoFsSRBkDq=x(YgEMpb=l4TDD`2 zwVJpWGTA_u7}?ecW7s6%rUs&NXD3+n;jB86`X?8(l3MBo6)PdakI6V6a}22{)8ilT zM~T*mU}__xSy|6XSrJ^%lDAR3Lft%+yxC|ZUvSO_nqMX!_ul3;R#*{~4DA=h$bP)%8Yv9X zyp><|e8=_ttI}ZAwOd#dlnSjck#6%273{E$kJuCGu=I@O)&6ID{nWF5@gLb16sj|&Sb~+du4e4O_%_o`Ix4NRrAsyr1_}MuP94s>de8cH-OUkVPk3+K z&jW)It9QiU-ti~AuJkL`XMca8Oh4$SyJ=`-5WU<{cIh+XVH#e4d&zive_UHC!pN>W z3TB;Mn5i)9Qn)#6@lo4QpI3jFYc0~+jS)4AFz8fVC;lD^+idw^S~Qhq>Tg(!3$yLD zzktzoFrU@6s4wwCMz}edpF5i5Q1IMmEJQHzp(LAt)pgN3&O!&d?3W@6U4)I^2V{;- z6A(?zd93hS*uQmnh4T)nHnE{wVhh(=MMD(h(P4+^p83Om6t<*cUW>l(qJzr%5vp@K zN27ka(L{JX=1~e2^)F^i=TYj&;<7jyUUR2Bek^A8+3Up*&Xwc{)1nRR5CT8vG>ExV zHnF3UqXJOAno_?bnhCX-&kwI~Ti8t4`n0%Up>!U`ZvK^w2+0Cs-b9%w%4`$+To|k= zKtgc&l}P`*8IS>8DOe?EB84^kx4BQp3<7P{Pq}&p%xF_81pg!l2|u=&I{AuUgmF5n zJQCTLv}%}xbFGYtKfbba{CBo)lWW%Z>i(_NvLhoQZ*5-@2l&x>e+I~0Nld3UI9tdL zRzu8}i;X!h8LHVvN?C+|M81e>Jr38%&*9LYQec9Ax>?NN+9(_>XSRv&6hlCYB`>Qm z1&ygi{Y()OU4@D_jd_-7vDILR{>o|7-k)Sjdxkjgvi{@S>6GqiF|o`*Otr;P)kLHN zZkpts;0zw_6;?f(@4S1FN=m!4^mv~W+lJA`&7RH%2$)49z0A+8@0BCHtj|yH--AEL z0tW6G%X-+J+5a{5*WKaM0QDznf;V?L5&uQw+yegDNDP`hA;0XPYc6e0;Xv6|i|^F2WB)Z$LR|HR4 zTQsRAby9(^Z@yATyOgcfQw7cKyr^3Tz7lc7+JEwwzA7)|2x+PtEb>nD(tpxJQm)Kn zW9K_*r!L%~N*vS8<5T=iv|o!zTe9k_2jC_j*7ik^M_ zaf%k{WX{-;0*`t`G!&`eW;gChVXnJ-Rn)To8vW-?>>a%QU1v`ZC=U)f8iA@%JG0mZ zDqH;~mgBnrCP~1II<=V9;EBL)J+xzCoiRBaeH&J6rL!{4zIY8tZka?_FBeQeNO3q6 zyG_alW54Ba&wQf{&F1v-r1R6ID)PTsqjIBc+5MHkcW5Fnvi~{-FjKe)t1bl}Y;z@< z=!%zvpRua>>t_x}^}z0<7MI!H2v6|XAyR9!t50q-A)xk0nflgF4*OQlCGK==4S|wc zRMsSscNhRzHMBU8TdcHN!q^I}x0iXJ%uehac|Zs_B$p@CnF)HeXPpB_Za}F{<@6-4 zl%kml@}kHQ(ypD8FsPJ2=14xXJE|b20RUIgs!2|R3>LUMGF6X*B_I|$`Qg=;zm7C z{mEDy9dTmPbued7mlO@phdmAmJ7p@GR1bjCkMw6*G7#4+`k>fk1czdJUB!e@Q(~6# zwo%@p@V5RL0ABU2LH7Asq^quDUho@H>eTZH9f*no9fY0T zD_-9px3e}A!>>kv5wk91%C9R1J_Nh!*&Kk$J3KNxC}c_@zlgpJZ+5L)Nw|^p=2ue}CJtm;uj*Iqr)K})kA$xtNUEvX;4!Px*^&9T_`IN{D z{6~QY=Nau6EzpvufB^hflc#XIsSq0Y9(nf$d~6ZwK}fal92)fr%T3=q{0mP-EyP_G z)UR5h@IX}3Qll2b0oCAcBF>b*@Etu*aTLPU<%C>KoOrk=x?pN!#f_Og-w+;xbFgjQ zXp`et%lDBBh~OcFnMKMUoox0YwBNy`N0q~bSPh@+enQ=4RUw1) zpovN`QoV>vZ#5LvC;cl|6jPr}O5tu!Ipoyib8iXqy}TeJ;4+_7r<1kV0v5?Kv>fYp zg>9L`;XwXa&W7-jf|9~uP2iyF5`5AJ`Q~p4eBU$MCC00`rcSF>`&0fbd^_eqR+}mK z4n*PMMa&FOcc)vTUR zlDUAn-mh`ahi_`f`=39JYTNVjsTa_Y3b1GOIi)6dY)D}xeshB0T8Eov5%UhWd1)u}kjEQ|LDo{tqKKrYIfVz~@dp!! zMOnah@vp)%_-jDTUG09l+;{CkDCH|Q{NqX*uHa1YxFShy*1+;J`gywKaz|2Q{lG8x zP?KBur`}r`!WLKXY_K;C8$EWG>jY3UIh{+BLv0=2)KH%P}6xE2kg)%(-uA6lC?u8}{K(#P*c zE9C8t*u%j2r_{;Rpe1A{9nNXU;b_N0vNgyK!EZVut~}+R2rcbsHilqsOviYh-pYX= zHw@53nlmwYI5W5KP>&`dBZe0Jn?nAdC^HY1wlR6$u^PbpB#AS&5L6zqrXN&7*N2Q` z+Rae1EwS)H=aVSIkr8Ek^1jy2iS2o7mqm~Mr&g5=jjt7VxwglQ^`h#Mx+x2v|9ZAwE$i_9918MjJxTMr?n!bZ6n$}y11u8I9COTU`Z$Fi z!AeAQLMw^gp_{+0QTEJrhL424pVDp%wpku~XRlD3iv{vQ!lAf!_jyqd_h}+Tr1XG| z`*FT*NbPqvHCUsYAkFnM`@l4u_QH&bszpUK#M~XLJt{%?00GXY?u_{gj3Hvs!=N(I z(=AuWPijyoU!r?aFTsa8pLB&cx}$*%;K$e*XqF{~*rA-qn)h^!(-;e}O#B$|S~c+U zN4vyOK0vmtx$5K!?g*+J@G1NmlEI=pyZXZ69tAv=@`t%ag_Hk{LP~OH9iE)I= zaJ69b4kuCkV0V zo(M0#>phpQ_)@j;h%m{-a*LGi(72TP)ws2w*@4|C-3+;=5DmC4s7Lp95%n%@Ko zfdr3-a7m*dys9iIci$A=4NPJ`HfJ;hujLgU)ZRuJI`n;Pw|yksu!#LQnJ#dJysgNb z@@qwR^wrk(jbq4H?d!lNyy72~Dnn87KxsgQ!)|*m(DRM+eC$wh7KnS-mho3|KE)7h zK3k;qZ;K1Lj6uEXLYUYi)1FN}F@-xJ z@@3Hb84sl|j{4$3J}aTY@cbX@pzB_qM~APljrjju6P0tY{C@ zpUCOz_NFmALMv1*blCcwUD3?U6tYs+N%cmJ98D%3)%)Xu^uvzF zS5O!sc#X6?EwsYkvPo6A%O8&y8sCCQH<%f2togVwW&{M;PR!a(ZT_A+jVAbf{@5kL zB@Z(hb$3U{T_}SKA_CoQVU-;j>2J=L#lZ~aQCFg-d<9rzs$_gO&d5N6eFSc z1ml8)P*FSi+k@!^M9nDWR5e@ATD8oxtDu=36Iv2!;dZzidIS(PCtEuXAtlBb1;H%Z zwnC^Ek*D)EX4#Q>R$$WA2sxC_t(!!6Tr?C#@{3}n{<^o;9id1RA&-Pig1e-2B1XpG zliNjgmd3c&%A}s>qf{_j#!Z`fu0xIwm4L0)OF=u(OEmp;bLCIaZX$&J_^Z%4Sq4GZ zPn6sV_#+6pJmDN_lx@1;Zw6Md_p0w9h6mHtzpuIEwNn>OnuRSC2=>fP^Hqgc)xu^4 z<3!s`cORHJh#?!nKI`Et7{3C27+EuH)Gw1f)aoP|B3y?fuVfvpYYmmukx0ya-)TQX zR{ggy5cNf4X|g)nl#jC9p>7|09_S7>1D2GTRBUTW zAkQ=JMRogZqG#v;^=11O6@rPPwvJkr{bW-Qg8`q8GoD#K`&Y+S#%&B>SGRL>;ZunM@49!}Uy zN|bBCJ%sO;@3wl0>0gbl3L@1^O60ONObz8ZI7nder>(udj-jt`;yj^nTQ$L9`OU9W zX4alF#$|GiR47%x@s&LV>2Sz2R6?;2R~5k6V>)nz!o_*1Y!$p>BC5&?hJg_MiE6UBy>RkVZj`9UWbRkN-Hk!S`=BS3t3uyX6)7SF#)71*}`~Ogz z1rap5H6~dhBJ83;q-Y<5V35C2&F^JI-it(=5D#v!fAi9p#UwV~2tZQI+W(Dv?1t9? zfh*xpxxO{-(VGB>!Q&0%^YW_F!@aZS#ucP|YaD#>wd1Fv&Z*SR&mc;asi}1G) z_H>`!akh-Zxq9#io(7%;a$)w+{QH)Y$?UK1Dt^4)up!Szcxnu}kn$0afcfJL#IL+S z5gF_Y30j;{lNrG6m~$Ay?)*V9fZuU@3=kd40=LhazjFrau>(Y>SJNtOz>8x_X-BlA zIpl{i>OarVGj1v(4?^1`R}aQB&WCRQzS~;7R{tDZG=HhgrW@B`W|#cdyj%YBky)P= zpxuOZkW>S6%q7U{VsB#G(^FMsH5QuGXhb(sY+!-R8Bmv6Sx3WzSW<1MPPN1!&PurYky(@`bP9tz z52}LH9Q?+FF5jR6-;|+GVdRA!qtd;}*-h&iIw3Tq3qF9sDIb1FFxGbo&fbG5n8$3F zyY&PWL{ys^dTO}oZ#@sIX^BKW*bon=;te9j5k+T%wJ zNJtoN1~YVj4~YRrlZl)b&kJqp+Z`DqT!la$x&&IxgOQw#yZd-nBP3!7FijBXD|IsU8Zl^ zc6?MKpJQ+7ka|tZQLfchD$PD|;K(9FiLE|eUZX#EZxhG!S-63C$jWX1Yd!6-Yxi-u zjULIr|0-Q%D9jz}IF~S%>0(jOqZ(Ln<$9PxiySr&2Oic7vb<8q=46)Ln%Z|<*z5&> z3f~Zw@m;vR(bESB<=Jqkxn(=#hQw42l(7)h`vMQQTttz9XW6^|^8EK7qhju4r_c*b zJIi`)MB$w@9epwdIfnEBR+?~);yd6C(LeMC& zn&&N*?-g&BBJcV;8&UoZi4Lmxcj16ojlxR~zMrf=O_^i1wGb9X-0@6_rpjPYemIin zmJb+;lHe;Yp=8G)Q(L1bzH*}I>}uAqhj4;g)PlvD9_e_ScR{Ipq|$8NvAvLD8MYr}xl=bU~)f%B3E>r3Bu9_t|ThF3C5~BdOve zEbk^r&r#PT&?^V1cb{72yEWH}TXEE}w>t!cY~rA+hNOTK8FAtIEoszp!qqptS&;r$ zaYV-NX96-h$6aR@1xz6_E0^N49mU)-v#bwtGJm)ibygzJ8!7|WIrcb`$XH~^!a#s& z{Db-0IOTFq#9!^j!n_F}#Z_nX{YzBK8XLPVmc&X`fT7!@$U-@2KM9soGbmOSAmqV z{nr$L^MBo_u^Joyf0E^=eo{Rt0{{e$IFA(#*kP@SQd6lWT2-#>` zP1)7_@IO!9lk>Zt?#CU?cuhiLF&)+XEM9B)cS(gvQT!X3`wL*{fArTS;Ak`J<84du zALKPz4}3nlG8Fo^MH0L|oK2-4xIY!~Oux~1sw!+It)&D3p;+N8AgqKI`ld6v71wy8I!eP0o~=RVcFQR2Gr(eP_JbSytoQ$Yt}l*4r@A8Me94y z8cTDWhqlq^qoAhbOzGBXv^Wa4vUz$(7B!mX`T=x_ueKRRDfg&Uc-e1+z4x$jyW_Pm zp?U;-R#xt^Z8Ev~`m`iL4*c#65Nn)q#=Y0l1AuD&+{|8-Gsij3LUZXpM0Bx0u7WWm zH|%yE@-#XEph2}-$-thl+S;__ciBxSSzHveP%~v}5I%u!z_l_KoW{KRx2=eB33umE zIYFtu^5=wGU`Jab8#}cnYry@9p5UE#U|VVvx_4l49JQ;jQdp(uw=$^A$EA$LM%vmE zvdEOaIcp5qX8wX{mYf0;#51~imYYPn4=k&#DsKTxo{_Mg*;S495?OBY?#gv=edYC* z^O@-sd-qa+U24xvcbL0@C7_6o!$`)sVr-jSJE4XQUQ$?L7}2(}Eixqv;L8AdJAVqc zq}RPgpnDb@E_;?6K58r3h4-!4rT4Ab#rLHLX?eMOfluJk=3i1@Gt1i#iA=O`M0@x! z(HtJP9BMHXEzuD93m|B&woj0g6T?f#^)>J>|I4C5?Gam>n9!8CT%~aT;=oco5d6U8 zMXl(=W;$ND_8+DD*?|5bJ!;8ebESXMUKBAf7YBwNVJibGaJ*(2G`F%wx)grqVPjudiaq^Kl&g$8A2 zWMxMr@_$c}d+;_B`#kUX-t|4VKH&_f^^EP0&=DPLW)H)UzBG%%Tra*5 z%$kyZe3I&S#gfie^z5)!twG={3Cuh)FdeA!Kj<-9** zvT*5%Tb`|QbE!iW-XcOuy39>D3oe6x{>&<#E$o8Ac|j)wq#kQzz|ATd=Z0K!p2$QE zPu?jL8Lb^y3_CQE{*}sTDe!2!dtlFjq&YLY@2#4>XS`}v#PLrpvc4*@q^O{mmnr5D zmyJq~t?8>FWU5vZdE(%4cuZuao0GNjp3~Dt*SLaxI#g_u>hu@k&9Ho*#CZP~lFJHj z(e!SYlLigyc?&5-YxlE{uuk$9b&l6d`uIlpg_z15dPo*iU&|Khx2*A5Fp;8iK_bdP z?T6|^7@lcx2j0T@x>X7|kuuBSB7<^zeY~R~4McconTxA2flHC0_jFxmSTv-~?zVT| zG_|yDqa9lkF*B6_{j=T>=M8r<0s;@z#h)3BQ4NLl@`Xr__o7;~M&dL3J8fP&zLfDfy z);ckcTev{@OUlZ`bCo(-3? z1u1xD`PKgSg?RqeVVsF<1SLF;XYA@Bsa&cY!I48ZJn1V<3d!?s=St?TLo zC0cNr`qD*M#s6f~X>SCNVkva^9A2ZP>CoJ9bvgXe_c}WdX-)pHM5m7O zrHt#g$F0AO+nGA;7dSJ?)|Mo~cf{z2L)Rz!`fpi73Zv)H=a5K)*$5sf_IZypi($P5 zsPwUc4~P-J1@^3C6-r9{V-u0Z&Sl7vNfmuMY4yy*cL>_)BmQF!8Om9Dej%cHxbIzA zhtV0d{=%cr?;bpBPjt@4w=#<>k5ee=TiWAXM2~tUGfm z$s&!Dm0R^V$}fOR*B^kGaipi~rx~A2cS0;t&khV1a4u38*XRUP~f za!rZMtay8bsLt6yFYl@>-y^31(*P!L^^s@mslZy(SMsv9bVoX`O#yBgEcjCmGpyc* zeH$Dw6vB5P*;jor+JOX@;6K#+xc)Z9B8M=x2a@Wx-{snPGpRmOC$zpsqW*JCh@M2Y z#K+M(>=#d^>Of9C`))h<=Bsy)6zaMJ&x-t%&+UcpLjV`jo4R2025 zXaG8EA!0lQa)|dx-@{O)qP6`$rhCkoQqZ`^SW8g-kOwrwsK8 z3ms*AIcyj}-1x&A&vSq{r=QMyp3CHdWH35!sad#!Sm>^|-|afB+Q;|Iq@LFgqIp#Z zD1%H+3I?6RGnk&IFo|u+E0dCxXz4yI^1i!QTu7uvIEH>i3rR{srcST`LIRwdV1P;W z+%AN1NIf@xxvVLiSX`8ILA8MzNqE&7>%jMzGt9wm78bo9<;h*W84i29^w!>V>{N+S zd`5Zmz^G;f=icvoOZfK5#1ctx*~UwD=ab4DGQXehQ!XYnak*dee%YN$_ZPL%KZuz$ zD;$PpT;HM^$KwtQm@7uvT`i6>Hae1CoRVM2)NL<2-k2PiX=eAx+-6j#JI?M}(tuBW zkF%jjLR)O`gI2fcPBxF^HeI|DWwQWHVR!;;{BXXHskxh8F@BMDn`oEi-NHt;CLymW z=KSv5)3dyzec0T5B*`g-MQ<;gz=nIWKUi9ko<|4I(-E0k$QncH>E4l z**1w&#={&zv4Tvhgz#c29`m|;lU-jmaXFMC11 z*dlXDMEOG>VoLMc>!rApwOu2prKSi*!w%`yzGmS+k(zm*CsLK*wv{S_0WX^8A-rKy zbk^Gf_92^7iB_uUF)EE+ET4d|X|>d&mdN?x@vxKAQk`O+r4Qdu>XGy(a(19g;=jU} zFX{O*_NG>!$@jh!U369Lnc+D~qch3uT+_Amyi}*k#LAAwh}k8IPK5a-WZ81ufD>l> z$4cF}GSz>ce`3FAic}6W4Z7m9KGO?(eWqi@L|5Hq0@L|&2flN1PVl}XgQ2q*_n2s3 zt5KtowNkTYB5b;SVuoXA@i5irXO)A&%7?V`1@HGCB&)Wgk+l|^XXChq;u(nyPB}b3 zY>m5jkxpZgi)zfbgv&ec4Zqdvm+D<?Im*mXweS9H+V>)zF#Zp3)bhl$PbISY{5=_z!8&*Jv~NYtI-g!>fDs zmvL5O^U%!^VaKA9gvKw|5?-jk>~%CVGvctKmP$kpnpfN{D8@X*Aazi$txfa%vd-|E z>kYmV66W!lNekJPom29LdZ%(I+ZLZYTXzTg*to~m?7vp%{V<~>H+2}PQ?PPAq`36R z<%wR8v6UkS>Wt#hzGk#44W<%9S=nBfB);6clKwnxY}T*w21Qc3_?IJ@4gYzC7s;WP zVQNI(M=S=JT#xsZy7G`cR(BP9*je0bfeN8JN5~zY(DDs0t{LpHOIbN);?T-69Pf3R zSNe*&p2%AwXHL>__g+xd4Hlc_vu<25H?(`nafS%)3UPP7_4;gk-9ckt8SJRTv5v0M z_Hww`qPudL?ajIR&X*;$y-`<)6dxx1U~5eGS13CB!lX;3w7n&lDDiArbAhSycd}+b zya_3p@A`$kQy;|NJZ~s44Hqo7Hwt}X86NK=(ey>lgWTtGL6k@Gy;PbO!M%1~Wcn2k zUFP|*5d>t-X*RU8g%>|(wwj*~#l4z^Aatf^DWd1Wj#Q*AY0D^V@sC`M zjJc6qXu0I7Y*2;;gGu!plAFzG=J;1%eIOdn zQA>J&e05UN*7I5@yRhK|lbBSfJ+5Uq;!&HV@xfPZrgD}kE*1DSq^=%{o%|LChhl#0 zlMb<^a6ixzpd{kNZr|3jTGeEzuo}-eLT-)Q$#b{!vKx8Tg}swCni>{#%vDY$Ww$84 zew3c9BBovqb}_&BRo#^!G(1Eg((BScRZ}C)Oz?y`T5wOrv);)b^4XR8 zhJo7+<^7)qB>I;46!GySzdneZ>n_E1oWZY;kf94#)s)kWjuJN1c+wbVoNQcmnv}{> zN0pF+Sl3E}UQ$}slSZeLJrwT>Sr}#V(dVaezCQl2|4LN`7L7v&siYR|r7M(*JYfR$ zst3=YaDw$FSc{g}KHO&QiKxuhEzF{f%RJLKe3p*7=oo`WNP)M(9X1zIQPP0XHhY3c znrP{$4#Ol$A0s|4S7Gx2L23dv*Gv2o;h((XVn+9+$qvm}s%zi6nI-_s6?mG! zj{DV;qesJb&owKeEK?=J>UcAlYckA7Sl+I&IN=yasrZOkejir*kE@SN`fk<8Fgx*$ zy&fE6?}G)d_N`){P~U@1jRVA|2*69)KSe_}!~?+`Yb{Y=O~_+@!j<&oVQQMnhoIRU zA0CyF1OFfkK44n*JD~!2!SCPM;PRSk%1XL=0&rz00wxPs&-_eapJy#$h!eqY%nS0{ z!aGg58JIJPF3_ci%n)QSVpa2H`vIe$RD43;#IRfDV&Ibit z+?>HW4{2wOfC6Fw)}4x}i1maDxcE1qi@BS*qcxD2gE@h3#4cgU*D-&3z7D|tVZWt= z-Cy2+*Cm@P4GN_TPUtaVyVesbVDazF@)j8VJ4>XZv!f%}&eO1SvIgr}4`A*3#vat< z_MoByL(qW6L7SFZ#|Gc1fFN)L2PxY+{B8tJp+pxRyz*87)vXR}*=&ahXjBlQKguuf zX6x<<6fQulE^C*KH8~W%ptpaC0l?b=_{~*U4?5Vt;dgM4t_{&UZ1C2j?b>b+5}{IF_CUyvz-@QZPMlJ)r_tS$9kH%RPv#2_nMb zRLj5;chJ72*U`Z@Dqt4$@_+k$%|8m(HqLG!qT4P^DdfvGf&){gKnGCX#H0!;W=AGP zbA&Z`-__a)VTS}kKFjWGk z%|>yE?t*EJ!qeQ%dPk$;xIQ+P0;()PCBDgjJm6Buj{f^awNoVx+9<|lg3%-$G(*f) zll6oOkN|yamn1uyl2*N-lnqRI1cvs_JxLTeahEK=THV$Sz*gQhKNb*p0fNoda#-&F zB-qJgW^g}!TtM|0bS2QZekW7_tKu%GcJ!4?lObt0z_$mZ4rbQ0o=^curCs3bJK6sq z9fu-aW-l#>z~ca(B;4yv;2RZ?tGYAU)^)Kz{L|4oPj zdOf_?de|#yS)p2v8-N||+XL=O*%3+y)oI(HbM)Ds?q8~HPzIP(vs*G`iddbWq}! z(2!VjP&{Z1w+%eUq^ '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/img-src/icon.svg b/img-src/icon.svg new file mode 100644 index 0000000..fe777cc --- /dev/null +++ b/img-src/icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/kavaref-android-stub/build.gradle.kts b/kavaref-android-stub/build.gradle.kts new file mode 100644 index 0000000..c1eb18f --- /dev/null +++ b/kavaref-android-stub/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("java") +} + +group = property.project.groupName + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} \ No newline at end of file diff --git a/kavaref-android-stub/src/main/java/android/util/Log.java b/kavaref-android-stub/src/main/java/android/util/Log.java new file mode 100644 index 0000000..2fad83b --- /dev/null +++ b/kavaref-android-stub/src/main/java/android/util/Log.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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. + */ + +package android.util; + +/** + * API for sending log output. + * + *

Generally, you should use the {@link #v Log.v()}, {@link #d Log.d()}, + * {@link #i Log.i()}, {@link #w Log.w()}, and {@link #e Log.e()} methods to write logs. + * You can then view the logs in logcat. + * + *

The order in terms of verbosity, from least to most is + * ERROR, WARN, INFO, DEBUG, VERBOSE. + * + *

Tip: A good convention is to declare a TAG constant + * in your class: + * + *

private static final String TAG = "MyActivity";
+ *

+ * and use that in subsequent calls to the log methods. + *

+ * + *

Tip: Don't forget that when you make a call like + *

Log.v(TAG, "index=" + i);
+ * that when you're building the string to pass into Log.d, the compiler uses a + * StringBuilder and at least three allocations occur: the StringBuilder + * itself, the buffer, and the String object. Realistically, there is also + * another buffer allocation and copy, and even more pressure on the gc. + * That means that if your log message is filtered out, you might be doing + * significant work and incurring significant overhead. + * + *

When calling the log methods that take a Throwable parameter, + * if any of the throwables in the cause chain is an UnknownHostException, + * then the stack trace is not logged. + * + *

Note: The return value from the logging functions in this class may vary between Android + * releases due to changes in the logging implementation. For the methods that return an integer, + * a positive value may be considered as a successful invocation. + */ +@SuppressWarnings("ALL") +public final class Log { + + private Log() { + } + + public static int v(String tag, String msg) { + throw new RuntimeException("Stub!"); + } + + public static int v(String tag, String msg, Throwable tr) { + throw new RuntimeException("Stub!"); + } + + public static int d(String tag, String msg) { + throw new RuntimeException("Stub!"); + } + + public static int d(String tag, String msg, Throwable tr) { + throw new RuntimeException("Stub!"); + } + + public static int i(String tag, String msg) { + throw new RuntimeException("Stub!"); + } + + public static int i(String tag, String msg, Throwable tr) { + throw new RuntimeException("Stub!"); + } + + public static int w(String tag, String msg) { + throw new RuntimeException("Stub!"); + } + + public static int w(String tag, String msg, Throwable tr) { + throw new RuntimeException("Stub!"); + } + + public static int w(String tag, Throwable tr) { + throw new RuntimeException("Stub!"); + } + + public static int e(String tag, String msg) { + throw new RuntimeException("Stub!"); + } + + public static int e(String tag, String msg, Throwable tr) { + throw new RuntimeException("Stub!"); + } + + public static int wtf(String tag, String msg) { + throw new RuntimeException("Stub!"); + } + + public static int wtfStack(String tag, String msg) { + throw new RuntimeException("Stub!"); + } + + public static int wtf(String tag, Throwable tr) { + throw new RuntimeException("Stub!"); + } + + public static int wtf(String tag, String msg, Throwable tr) { + throw new RuntimeException("Stub!"); + } +} diff --git a/kavaref-core/build.gradle.kts b/kavaref-core/build.gradle.kts new file mode 100644 index 0000000..03ace43 --- /dev/null +++ b/kavaref-core/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + autowire(libs.plugins.kotlin.jvm) + autowire(libs.plugins.kotlin.dokka) + autowire(libs.plugins.maven.publish) +} + +group = property.project.groupName +version = property.project.kavaref.core.version + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + withSourcesJar() +} + +kotlin { + jvmToolchain(17) + sourceSets.all { languageSettings { languageVersion = "2.0" } } + compilerOptions { + freeCompilerArgs = listOf( + "-Xno-param-assertions", + "-Xno-call-assertions", + "-Xno-receiver-assertions" + ) + } +} + +dependencies { + // Android Only + compileOnly(projects.kavarefAndroidStub) + implementation(projects.kavarefExtension) + implementation(org.slf4j.slf4j.api) + implementation(org.slf4j.slf4j.simple) +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/KavaRef.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/KavaRef.kt new file mode 100644 index 0000000..dac05cf --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/KavaRef.kt @@ -0,0 +1,433 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +@file:Suppress("unused", "MemberVisibilityCanBePrivate", "UNCHECKED_CAST", "UnusedReceiverParameter", "DeprecatedCallableAddReplaceWith") + +package com.highcapable.kavaref + +import com.highcapable.kavaref.KavaRef.Companion.resolve +import com.highcapable.kavaref.condition.ConstructorCondition +import com.highcapable.kavaref.condition.FieldCondition +import com.highcapable.kavaref.condition.MethodCondition +import com.highcapable.kavaref.condition.base.MemberCondition +import com.highcapable.kavaref.condition.base.MemberCondition.Configuration.Companion.createConfiguration +import com.highcapable.kavaref.resolver.ConstructorResolver +import com.highcapable.kavaref.resolver.FieldResolver +import com.highcapable.kavaref.resolver.MethodResolver +import com.highcapable.kavaref.resolver.base.MemberResolver +import com.highcapable.kavaref.resolver.processor.MemberProcessor +import com.highcapable.kavaref.runtime.KavaRefRuntime +import com.highcapable.kavaref.runtime.KavaRefRuntime.Logger +import kotlin.reflect.KClass + +/** + * This is the class created and managed by KavaRef. + * + * Try to use [KavaRef.resolve] to start a new reflection. + */ +class KavaRef private constructor() { + + companion object { + + /** Get or set the log level for KavaRef. */ + @JvmStatic + var logLevel by KavaRefRuntime::logLevel + + /** + * Set the logger for KavaRef. + * @param logger the logger to be set. + */ + @JvmStatic + fun setLogger(logger: Logger) = KavaRefRuntime.setLogger(logger) + + /** + * Create a [MemberScope] instance to start a new reflection. + * @receiver the [KClass.java] to be reflected. + * @return [MemberScope] + */ + @JvmSynthetic + fun KClass.resolve() = MemberScope(java.createConfiguration()) + + /** + * Create a [MemberScope] with a block to start a new reflection. + * @receiver the [KClass.java] to be reflected. + * @param block the block to configure the [MemberScope]. + * @return [MemberScope] + */ + inline fun KClass.resolve(block: MemberScope.() -> Unit) = resolve().apply(block) + + /** + * Create a [MemberScope] instance to start a new reflection. + * @receiver the [Class] to be reflected. + * @return [MemberScope] + */ + @JvmStatic + @JvmName("resolveClass") + fun Class.resolve() = MemberScope(createConfiguration()) + + /** + * Create a [MemberScope] instance to start a new reflection. + * @see KClass.resolve + * @see Class.resolve + * @return [MemberScope] + */ + @JvmStatic + @JvmName("resolveObject") + fun T.resolve() = when (this) { + is KClass<*> -> MemberScope((this as KClass).java.createConfiguration(memberInstance = this)) + is Class<*> -> MemberScope((this as Class).createConfiguration(memberInstance = this)) + else -> MemberScope(javaClass.createConfiguration(memberInstance = this)) + } + + // Below are deprecated functions to prevent recursive calls. + + private const val RECURSIVELY_CALL_DEPRECATED_MESSAGE = "You are calling resolve() recursively, it's an error and should delete it." + private const val RECURSIVELY_CALL_EXCEPTION_MESSAGE = "Not allowed to call resolve() recursively, please delete it." + + /** + * This is a fake function call chains to avoid recursive calls to themselves. + */ + @Deprecated(message = RECURSIVELY_CALL_DEPRECATED_MESSAGE, level = DeprecationLevel.ERROR) + @JvmSynthetic + fun MemberScope<*>.resolve(): MemberScope<*> = error(RECURSIVELY_CALL_EXCEPTION_MESSAGE) + + /** + * This is a fake function call chains to avoid recursive calls to themselves. + */ + @Deprecated(message = RECURSIVELY_CALL_DEPRECATED_MESSAGE, level = DeprecationLevel.ERROR) + @JvmSynthetic + fun MemberCondition<*, *, *>.resolve(): MemberCondition<*, *, *> = error(RECURSIVELY_CALL_EXCEPTION_MESSAGE) + + /** + * This is a fake function call chains to avoid recursive calls to themselves. + */ + @Deprecated(message = RECURSIVELY_CALL_DEPRECATED_MESSAGE, level = DeprecationLevel.ERROR) + @JvmSynthetic + fun MemberResolver<*, *>.resolve(): MemberResolver<*, *> = error(RECURSIVELY_CALL_EXCEPTION_MESSAGE) + + /** + * This is a fake function call chains to avoid recursive calls to themselves. + */ + @Deprecated(message = RECURSIVELY_CALL_DEPRECATED_MESSAGE, level = DeprecationLevel.ERROR) + @JvmSynthetic + fun List>.resolve(): List> = error(RECURSIVELY_CALL_EXCEPTION_MESSAGE) + } + + /** + * The KavaRef scope for member reflection. + * + * [T] to specify the declaring class type of the member. + * @param configuration the configuration to be reflected. + */ + class MemberScope internal constructor(private val configuration: MemberCondition.Configuration) { + + /** + * Set the [MemberProcessor.Resolver] to be used for this reflection. + * @see MemberCondition.Configuration.processorResolver + * @see MemberProcessor.Resolver + * @param resolver the resolver to be used. + */ + fun processor(resolver: MemberProcessor.Resolver) = apply { + configuration.processorResolver = resolver + } + + /** + * Enable optional mode. + * @see MemberCondition.Configuration.optional + * @param silent see [MemberCondition.Configuration.Optional.SILENT] + */ + fun optional(silent: Boolean = false) = apply { + configuration.optional = if (silent) + MemberCondition.Configuration.Optional.SILENT + else MemberCondition.Configuration.Optional.NOTICE + } + + /** + * Start a new method reflection. + * @return [MethodCondition] + */ + fun method() = MethodCondition().also { + it.configuration = configuration + } + + /** + * Start a new method reflection. + * @see firstMethod + * @see firstMethodOrNull + * @param condition the condition. + * @return [MethodResolver] + */ + fun method(condition: MethodCondition) = condition.build(configuration) + + /** + * Start a new method reflection and return the first matching method. + * @see method + * @param condition the condition body. + * @return [MethodResolver] + */ + fun firstMethod(condition: MethodCondition) = method(condition).first() + + /** + * Start a new method reflection and return the first matching method or null. + * @see method + * @param condition the condition body. + * @return [MethodResolver] or null. + */ + fun firstMethodOrNull(condition: MethodCondition) = method(condition).firstOrNull() + + /** + * Start a new method reflection and return the last matching method. + * @see method + * @param condition the condition body. + * @return [MethodResolver] + */ + fun lastMethod(condition: MethodCondition) = method(condition).last() + + /** + * Start a new method reflection and return the last matching method or null. + * @see method + * @param condition the condition body. + * @return [MethodResolver] or null. + */ + fun lastMethodOrNull(condition: MethodCondition) = method(condition).lastOrNull() + + /** + * Start a new method reflection. + * @see firstMethod + * @see firstMethodOrNull + * @param condition the condition body. + * @return [MethodResolver] + */ + inline fun method(condition: MethodCondition.() -> Unit) = method().apply(condition).build() + + /** + * Start a new method reflection and return the first matching method. + * @see method + * @param condition the condition body. + * @return [MethodResolver] + */ + inline fun firstMethod(condition: MethodCondition.() -> Unit = {}) = method(condition).first() + + /** + * Start a new method reflection and return the first matching method or null. + * @see method + * @param condition the condition body. + * @return [MethodResolver] or null. + */ + inline fun firstMethodOrNull(condition: MethodCondition.() -> Unit = {}) = method(condition).firstOrNull() + + /** + * Start a new method reflection and return the last matching method. + * @see method + * @param condition the condition body. + * @return [MethodResolver] + */ + inline fun lastMethod(condition: MethodCondition.() -> Unit = {}) = method(condition).last() + + /** + * Start a new method reflection and return the last matching method or null. + * @see method + * @param condition the condition body. + * @return [MethodResolver] or null. + */ + inline fun lastMethodOrNull(condition: MethodCondition.() -> Unit = {}) = method(condition).lastOrNull() + + /** + * Start a new constructor reflection. + * @return [ConstructorCondition] + */ + fun constructor() = ConstructorCondition().also { + it.configuration = configuration + } + + /** + * Start a new constructor reflection. + * @see firstConstructor + * @see firstConstructorOrNull + * @param condition the condition. + * @return [ConstructorResolver] + */ + fun constructor(condition: ConstructorCondition) = condition.build(configuration) + + /** + * Start a new constructor reflection and return the first matching constructor. + * @see constructor + * @param condition the condition body. + * @return [ConstructorResolver] + */ + fun firstConstructor(condition: ConstructorCondition) = constructor(condition).first() + + /** + * Start a new constructor reflection and return the first matching constructor or null. + * @see constructor + * @param condition the condition body. + * @return [ConstructorResolver] or null. + */ + fun firstConstructorOrNull(condition: ConstructorCondition) = constructor(condition).firstOrNull() + + /** + * Start a new constructor reflection and return the last matching constructor. + * @see constructor + * @param condition the condition body. + * @return [ConstructorResolver] + */ + fun lastConstructor(condition: ConstructorCondition) = constructor(condition).last() + + /** + * Start a new constructor reflection and return the last matching constructor or null. + * @see constructor + * @param condition the condition body. + * @return [ConstructorResolver] or null. + */ + fun lastConstructorOrNull(condition: ConstructorCondition) = constructor(condition).lastOrNull() + + /** + * Start a new constructor reflection. + * @see firstConstructor + * @see firstConstructorOrNull + * @param condition the condition body. + * @return [ConstructorResolver] + */ + inline fun constructor(condition: ConstructorCondition.() -> Unit) = constructor().apply(condition).build() + + /** + * Start a new constructor reflection and return the first matching constructor. + * @see constructor + * @param condition the condition body. + * @return [ConstructorResolver] + */ + inline fun firstConstructor(condition: ConstructorCondition.() -> Unit = {}) = constructor(condition).first() + + /** + * Start a new constructor reflection and return the first matching constructor or null. + * @see constructor + * @param condition the condition body. + * @return [ConstructorResolver] or null. + */ + inline fun firstConstructorOrNull(condition: ConstructorCondition.() -> Unit = {}) = constructor(condition).firstOrNull() + + /** + * Start a new constructor reflection and return the last matching constructor. + * @see constructor + * @param condition the condition body. + * @return [ConstructorResolver] + */ + inline fun lastConstructor(condition: ConstructorCondition.() -> Unit = {}) = constructor(condition).last() + + /** + * Start a new constructor reflection and return the last matching constructor or null. + * @see constructor + * @param condition the condition body. + * @return [ConstructorResolver] or null. + */ + inline fun lastConstructorOrNull(condition: ConstructorCondition.() -> Unit = {}) = constructor(condition).lastOrNull() + + /** + * Start a new field reflection. + * @return [FieldCondition] + */ + fun field() = FieldCondition().also { + it.configuration = configuration + } + + /** + * Start a new field reflection. + * @see firstField + * @see firstFieldOrNull + * @param condition the condition. + * @return [FieldResolver] + */ + fun field(condition: FieldCondition) = condition.build(configuration) + + /** + * Start a new field reflection and return the first matching field. + * @see field + * @param condition the condition body. + * @return [FieldResolver] + */ + fun firstField(condition: FieldCondition) = field(condition).first() + + /** + * Start a new field reflection and return the first matching field or null. + * @see field + * @param condition the condition body. + * @return [FieldResolver] or null. + */ + fun firstFieldOrNull(condition: FieldCondition) = field(condition).firstOrNull() + + /** + * Start a new field reflection and return the last matching field. + * @see field + * @param condition the condition body. + * @return [FieldResolver] + */ + fun lastField(condition: FieldCondition) = field(condition).last() + + /** + * Start a new field reflection and return the last matching field or null. + * @see field + * @param condition the condition body. + * @return [FieldResolver] or null. + */ + fun lastFieldOrNull(condition: FieldCondition) = field(condition).lastOrNull() + + /** + * Start a new field reflection. + * @see firstField + * @see firstFieldOrNull + * @param condition the condition body. + * @return [FieldResolver] + */ + inline fun field(condition: FieldCondition.() -> Unit) = field().apply(condition).build() + + /** + * Start a new field reflection and return the first matching field. + * @see field + * @param condition the condition body. + * @return [FieldResolver] + */ + inline fun firstField(condition: FieldCondition.() -> Unit = {}) = field(condition).first() + + /** + * Start a new field reflection and return the first matching field or null. + * @see field + * @param condition the condition body. + * @return [FieldResolver] or null. + */ + inline fun firstFieldOrNull(condition: FieldCondition.() -> Unit = {}) = field(condition).firstOrNull() + + /** + * Start a new field reflection and return the last matching field. + * @see field + * @param condition the condition body. + * @return [FieldResolver] + */ + inline fun lastField(condition: FieldCondition.() -> Unit = {}) = field(condition).last() + + /** + * Start a new field reflection and return the last matching field or null. + * @see field + * @param condition the condition body. + * @return [FieldResolver] or null. + */ + inline fun lastFieldOrNull(condition: FieldCondition.() -> Unit = {}) = field(condition).lastOrNull() + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/ConstructorCondition.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/ConstructorCondition.kt new file mode 100644 index 0000000..136340e --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/ConstructorCondition.kt @@ -0,0 +1,87 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +package com.highcapable.kavaref.condition + +import com.highcapable.kavaref.condition.base.ExecutableCondition +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import com.highcapable.kavaref.condition.type.Modifiers +import com.highcapable.kavaref.resolver.ConstructorResolver +import com.highcapable.kavaref.resolver.processor.MemberProcessor +import java.lang.reflect.Constructor + +/** + * Condition for [Constructor] of [ConstructorResolver]. + */ +class ConstructorCondition : ExecutableCondition, ConstructorResolver, T>() { + + override fun name(name: String) = apply { super.name(name) } + override fun name(condition: (String) -> Boolean) = apply { super.name(condition) } + override fun modifiers(vararg modifiers: Modifiers) = apply { super.modifiers(*modifiers) } + override fun modifiersNot(vararg modifiers: Modifiers) = apply { super.modifiersNot(*modifiers) } + override fun modifiers(condition: (Set) -> Boolean) = apply { super.modifiers(condition) } + override fun isSynthetic(isSynthetic: Boolean) = apply { super.isSynthetic(isSynthetic) } + override fun isSyntheticNot(isSynthetic: Boolean) = apply { super.isSyntheticNot(isSynthetic) } + override fun annotations(vararg annotations: Any) = apply { super.annotations(*annotations) } + override fun annotationsNot(vararg annotations: Any) = apply { super.annotationsNot(*annotations) } + override fun genericString(genericString: String) = apply { super.genericString(genericString) } + + override fun parameters(vararg types: Any) = apply { super.parameters(*types) } + override fun parametersNot(vararg types: Any) = apply { super.parametersNot(*types) } + override fun parameters(condition: (List>) -> Boolean) = apply { super.parameters(condition) } + override fun emptyParameters() = apply { super.emptyParameters() } + override fun emptyParametersNot() = apply { super.emptyParametersNot() } + override fun typeParameters(vararg types: TypeMatcher) = apply { super.typeParameters(*types) } + override fun typeParametersNot(vararg types: TypeMatcher) = apply { super.typeParametersNot(*types) } + override fun parameterCount(count: Int) = apply { super.parameterCount(count) } + override fun parameterCount(condition: (Int) -> Boolean) = apply { super.parameterCount(condition) } + override fun exceptionTypes(vararg types: Any) = apply { super.exceptionTypes(*types) } + override fun exceptionTypesNot(vararg types: Any) = apply { super.exceptionTypesNot(*types) } + override fun genericExceptionTypes(vararg types: TypeMatcher) = apply { super.genericExceptionTypes(*types) } + override fun genericExceptionTypesNot(vararg types: TypeMatcher) = apply { super.genericExceptionTypesNot(*types) } + override fun genericParameters(vararg types: TypeMatcher) = apply { super.genericParameters(*types) } + override fun genericParametersNot(vararg types: TypeMatcher) = apply { super.genericParametersNot(*types) } + override fun isVarArgs(isVarArgs: Boolean) = apply { super.isVarArgs(isVarArgs) } + override fun isVarArgsNot(isVarArgs: Boolean) = apply { super.isVarArgsNot(isVarArgs) } + override fun parameterAnnotations(vararg annotations: Set) = apply { super.parameterAnnotations(*annotations) } + override fun parameterAnnotationsNot(vararg annotations: Set) = apply { super.parameterAnnotationsNot(*annotations) } + override fun annotatedReturnType(vararg types: Any) = apply { super.annotatedReturnType(*types) } + override fun annotatedReturnTypeNot(vararg types: Any) = apply { super.annotatedReturnTypeNot(*types) } + override fun annotatedReceiverType(vararg types: Any) = apply { super.annotatedReceiverType(*types) } + override fun annotatedReceiverTypeNot(vararg types: Any) = apply { super.annotatedReceiverTypeNot(*types) } + override fun annotatedParameterTypes(vararg types: Any) = apply { super.annotatedParameterTypes(*types) } + override fun annotatedParameterTypesNot(vararg types: Any) = apply { super.annotatedParameterTypesNot(*types) } + override fun annotatedExceptionTypes(vararg types: Any) = apply { super.annotatedExceptionTypes(*types) } + override fun annotatedExceptionTypesNot(vararg types: Any) = apply { super.annotatedExceptionTypesNot(*types) } + + override fun superclass() = apply { super.superclass() } + + override fun copy() = ConstructorCondition().also { + initializeCopiedData(it) + } + + override fun build(configuration: Configuration?): List> { + configuration?.let { checkAndSetConfiguration(it) } + return MemberProcessor.resolve(condition = this, this.configuration) + } + + override val conditionStringMap get() = super.conditionStringMap +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/FieldCondition.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/FieldCondition.kt new file mode 100644 index 0000000..5604598 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/FieldCondition.kt @@ -0,0 +1,153 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +@file:Suppress("unused", "DuplicatedCode") + +package com.highcapable.kavaref.condition + +import com.highcapable.kavaref.condition.base.MemberCondition +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import com.highcapable.kavaref.condition.type.Modifiers +import com.highcapable.kavaref.resolver.FieldResolver +import com.highcapable.kavaref.resolver.processor.MemberProcessor +import java.lang.reflect.Field +import java.lang.reflect.Type + +/** + * Condition for [Field] of [FieldResolver]. + */ +class FieldCondition : MemberCondition, T>() { + + /** @see Field.isEnumConstant */ + var isEnumConstant: Boolean? = null + + /** @see Field.isEnumConstant */ + var isEnumConstantNot: Boolean? = null + + /** @see Field.getType */ + var type: Any? = null + + /** @see Field.getType */ + var typeCondition: ((Class<*>) -> Boolean)? = null + + /** @see Field.getGenericType */ + var genericType: TypeMatcher? = null + + /** @see Field.getGenericType */ + var genericTypeCondition: ((Type) -> Boolean)? = null + + override fun name(name: String) = apply { super.name(name) } + override fun name(condition: (String) -> Boolean) = apply { super.name(condition) } + override fun modifiers(vararg modifiers: Modifiers) = apply { super.modifiers(*modifiers) } + override fun modifiersNot(vararg modifiers: Modifiers) = apply { super.modifiersNot(*modifiers) } + override fun modifiers(condition: (Set) -> Boolean) = apply { super.modifiers(condition) } + override fun isSynthetic(isSynthetic: Boolean) = apply { super.isSynthetic(isSynthetic) } + override fun isSyntheticNot(isSynthetic: Boolean) = apply { super.isSyntheticNot(isSynthetic) } + override fun annotations(vararg annotations: Any) = apply { super.annotations(*annotations) } + override fun annotationsNot(vararg annotations: Any) = apply { super.annotationsNot(*annotations) } + override fun genericString(genericString: String) = apply { super.genericString(genericString) } + + override fun superclass() = apply { super.superclass() } + + /** @see Field.isEnumConstant */ + fun isEnumConstant(isEnumConstant: Boolean) = apply { + this.isEnumConstant = isEnumConstant + } + + /** @see Field.isEnumConstant */ + fun isEnumConstantNot(isEnumConstant: Boolean) = apply { + this.isEnumConstantNot = isEnumConstant + } + + /** @see Field.getType */ + fun type(type: Any) = apply { + this.type = type + } + + /** @see Field.getType */ + fun type(condition: (Class<*>) -> Boolean) = apply { + this.typeCondition = condition + } + + /** @see Field.getGenericType */ + fun genericType(type: TypeMatcher) = apply { + this.genericType = type + } + + /** @see Field.getGenericType */ + fun genericType(condition: (Type) -> Boolean) = apply { + this.genericTypeCondition = condition + } + + override fun initializeCopiedData(newSelf: MemberCondition, T>) { + super.initializeCopiedData(newSelf) + + (newSelf as? FieldCondition)?.also { + it.isEnumConstant = isEnumConstant + it.isEnumConstantNot = isEnumConstantNot + it.type = type + it.typeCondition = typeCondition + it.genericType = genericType + it.genericTypeCondition = genericTypeCondition + } + } + + override fun initializeMergedData(other: MemberCondition, T>) { + super.initializeMergedData(other) + + (other as? FieldCondition)?.also { condition -> + condition.isEnumConstant?.let { isEnumConstant = it } + condition.isEnumConstantNot?.let { isEnumConstantNot = it } + condition.type?.let { type = it } + condition.typeCondition?.let { typeCondition = it } + condition.genericType?.let { genericType = it } + condition.genericTypeCondition?.let { genericTypeCondition = it } + } + } + + override fun copy() = FieldCondition().also { + initializeCopiedData(it) + } + + override fun build(configuration: Configuration?): List> { + configuration?.let { checkAndSetConfiguration(it) } + return MemberProcessor.resolve(condition = this, this.configuration) + } + + override val conditionStringMap + get() = super.conditionStringMap + mapOf( + IS_ENUM_CONSTANT to isEnumConstant, + IS_ENUM_CONSTANT_NOT to isEnumConstantNot, + TYPE to type, + TYPE_CONDITION to typeCondition, + GENERIC_TYPE to genericType, + GENERIC_TYPE_CONDITION to genericTypeCondition + ) + + companion object { + const val IS_ENUM_CONSTANT = "isEnumConstant" + const val IS_ENUM_CONSTANT_NOT = "isEnumConstantNot" + const val TYPE = "type" + const val TYPE_CONDITION = "typeCondition" + const val GENERIC_TYPE = "genericType" + const val GENERIC_TYPE_CONDITION = "genericTypeCondition" + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/MethodCondition.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/MethodCondition.kt new file mode 100644 index 0000000..c57eccf --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/MethodCondition.kt @@ -0,0 +1,181 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +@file:Suppress("unused", "DuplicatedCode") + +package com.highcapable.kavaref.condition + +import com.highcapable.kavaref.condition.base.ExecutableCondition +import com.highcapable.kavaref.condition.base.MemberCondition +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import com.highcapable.kavaref.condition.type.Modifiers +import com.highcapable.kavaref.resolver.MethodResolver +import com.highcapable.kavaref.resolver.processor.MemberProcessor +import java.lang.reflect.Method + +/** + * Condition for [Method] of [MethodResolver]. + */ +class MethodCondition : ExecutableCondition, T>() { + + /** @see Method.getReturnType */ + var returnType: Any? = null + + /** @see Method.getReturnType */ + var returnTypeCondition: ((Class<*>) -> Boolean)? = null + + /** @see Method.isBridge */ + var isBridge: Boolean? = null + + /** @see Method.isBridge */ + var isBridgeNot: Boolean? = null + + /** @see Method.isDefault */ + var isDefault: Boolean? = null + + /** @see Method.isDefault */ + var isDefaultNot: Boolean? = null + + override fun name(name: String) = apply { super.name(name) } + override fun name(condition: (String) -> Boolean) = apply { super.name(condition) } + override fun modifiers(vararg modifiers: Modifiers) = apply { super.modifiers(*modifiers) } + override fun modifiersNot(vararg modifiers: Modifiers) = apply { super.modifiersNot(*modifiers) } + override fun modifiers(condition: (Set) -> Boolean) = apply { super.modifiers(condition) } + override fun isSynthetic(isSynthetic: Boolean) = apply { super.isSynthetic(isSynthetic) } + override fun isSyntheticNot(isSynthetic: Boolean) = apply { super.isSyntheticNot(isSynthetic) } + override fun annotations(vararg annotations: Any) = apply { super.annotations(*annotations) } + override fun annotationsNot(vararg annotations: Any) = apply { super.annotationsNot(*annotations) } + override fun genericString(genericString: String) = apply { super.genericString(genericString) } + + override fun parameters(vararg types: Any) = apply { super.parameters(*types) } + override fun parametersNot(vararg types: Any) = apply { super.parametersNot(*types) } + override fun parameters(condition: (List>) -> Boolean) = apply { super.parameters(condition) } + override fun emptyParameters() = apply { super.emptyParameters() } + override fun emptyParametersNot() = apply { super.emptyParametersNot() } + override fun typeParameters(vararg types: TypeMatcher) = apply { super.typeParameters(*types) } + override fun typeParametersNot(vararg types: TypeMatcher) = apply { super.typeParametersNot(*types) } + override fun parameterCount(count: Int) = apply { super.parameterCount(count) } + override fun parameterCount(condition: (Int) -> Boolean) = apply { super.parameterCount(condition) } + override fun exceptionTypes(vararg types: Any) = apply { super.exceptionTypes(*types) } + override fun exceptionTypesNot(vararg types: Any) = apply { super.exceptionTypesNot(*types) } + override fun genericExceptionTypes(vararg types: TypeMatcher) = apply { super.genericExceptionTypes(*types) } + override fun genericExceptionTypesNot(vararg types: TypeMatcher) = apply { super.genericExceptionTypesNot(*types) } + override fun genericParameters(vararg types: TypeMatcher) = apply { super.genericParameters(*types) } + override fun genericParametersNot(vararg types: TypeMatcher) = apply { super.genericParametersNot(*types) } + override fun isVarArgs(isVarArgs: Boolean) = apply { super.isVarArgs(isVarArgs) } + override fun isVarArgsNot(isVarArgs: Boolean) = apply { super.isVarArgsNot(isVarArgs) } + override fun parameterAnnotations(vararg annotations: Set) = apply { super.parameterAnnotations(*annotations) } + override fun parameterAnnotationsNot(vararg annotations: Set) = apply { super.parameterAnnotationsNot(*annotations) } + override fun annotatedReturnType(vararg types: Any) = apply { super.annotatedReturnType(*types) } + override fun annotatedReturnTypeNot(vararg types: Any) = apply { super.annotatedReturnTypeNot(*types) } + override fun annotatedReceiverType(vararg types: Any) = apply { super.annotatedReceiverType(*types) } + override fun annotatedReceiverTypeNot(vararg types: Any) = apply { super.annotatedReceiverTypeNot(*types) } + override fun annotatedParameterTypes(vararg types: Any) = apply { super.annotatedParameterTypes(*types) } + override fun annotatedParameterTypesNot(vararg types: Any) = apply { super.annotatedParameterTypesNot(*types) } + override fun annotatedExceptionTypes(vararg types: Any) = apply { super.annotatedExceptionTypes(*types) } + override fun annotatedExceptionTypesNot(vararg types: Any) = apply { super.annotatedExceptionTypesNot(*types) } + + override fun superclass() = apply { super.superclass() } + + /** @see Method.getReturnType */ + fun returnType(type: Any) = apply { + this.returnType = type + } + + /** @see Method.getReturnType */ + fun returnType(condition: (Class<*>) -> Boolean) = apply { + this.returnTypeCondition = condition + } + + /** @see Method.isBridge */ + fun isBridge(isBridge: Boolean) = apply { + this.isBridge = isBridge + } + + /** @see Method.isBridge */ + fun isBridgeNot(isBridge: Boolean) = apply { + this.isBridgeNot = isBridge + } + + /** @see Method.isDefault */ + fun isDefault(isDefault: Boolean) = apply { + this.isDefault = isDefault + } + + /** @see Method.isDefault */ + fun isDefaultNot(isDefault: Boolean) = apply { + this.isDefaultNot = isDefault + } + + override fun initializeCopiedData(newSelf: MemberCondition, T>) { + super.initializeCopiedData(newSelf) + + (newSelf as? MethodCondition)?.also { + it.returnType = returnType + it.returnTypeCondition = returnTypeCondition + it.isBridge = isBridge + it.isBridgeNot = isBridgeNot + it.isDefault = isDefault + it.isDefaultNot = isDefaultNot + } + } + + override fun initializeMergedData(other: MemberCondition, T>) { + super.initializeMergedData(other) + + (other as? MethodCondition)?.also { condition -> + condition.returnType?.let { returnType = it } + condition.returnTypeCondition?.let { returnTypeCondition = it } + condition.isBridge?.let { isBridge = it } + condition.isBridgeNot?.let { isBridgeNot = it } + condition.isDefault?.let { isDefault = it } + condition.isDefaultNot?.let { isDefaultNot = it } + } + } + + override fun copy() = MethodCondition().also { + initializeCopiedData(it) + } + + override fun build(configuration: Configuration?): List> { + configuration?.let { checkAndSetConfiguration(it) } + return MemberProcessor.resolve(condition = this, this.configuration) + } + + override val conditionStringMap + get() = super.conditionStringMap + mapOf( + RETURN_TYPE to returnType, + RETURN_TYPE_CONDITION to returnTypeCondition, + IS_BRIDGE to isBridge, + IS_BRIDGE_NOT to isBridgeNot, + IS_DEFAULT to isDefault, + IS_DEFAULT_NOT to isDefaultNot + ) + + companion object { + const val RETURN_TYPE = "returnType" + const val RETURN_TYPE_CONDITION = "returnTypeCondition" + const val IS_BRIDGE = "isBridge" + const val IS_BRIDGE_NOT = "isBridgeNot" + const val IS_DEFAULT = "isDefault" + const val IS_DEFAULT_NOT = "isDefaultNot" + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/base/ExecutableCondition.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/base/ExecutableCondition.kt new file mode 100644 index 0000000..753adeb --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/base/ExecutableCondition.kt @@ -0,0 +1,433 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +@file:Suppress("DuplicatedCode") + +package com.highcapable.kavaref.condition.base + +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import com.highcapable.kavaref.resolver.base.MemberResolver +import java.lang.reflect.Executable + +/** + * Base class for conditions that are applicable to executable members (methods and constructors) [E], [R]. + * + * [T] to specify the declaring class type of the executable. + */ +abstract class ExecutableCondition, T : Any> : MemberCondition() { + + /** @see Executable.getParameterTypes */ + val parameters = mutableListOf() + + /** @see Executable.getParameterTypes */ + val parametersNot = mutableListOf() + + /** @see Executable.getParameterTypes */ + var parametersCondition: ((List>) -> Boolean)? = null + + /** @see Executable.getTypeParameters */ + val typeParameters = mutableSetOf() + + /** @see Executable.getTypeParameters */ + val typeParametersNot = mutableSetOf() + + /** @see Executable.getParameterCount */ + var parameterCount: Int? = null + + /** @see Executable.getParameterCount */ + var parameterCountCondition: ((Int) -> Boolean)? = null + + /** @see Executable.getExceptionTypes */ + val exceptionTypes = mutableSetOf() + + /** @see Executable.getExceptionTypes */ + val exceptionTypesNot = mutableSetOf() + + /** @see Executable.getGenericExceptionTypes */ + val genericExceptionTypes = mutableSetOf() + + /** @see Executable.getGenericExceptionTypes */ + val genericExceptionTypesNot = mutableSetOf() + + /** @see Executable.getGenericParameterTypes */ + val genericParameters = mutableSetOf() + + /** @see Executable.getGenericParameterTypes */ + val genericParametersNot = mutableSetOf() + + /** @see Executable.isVarArgs */ + var isVarArgs: Boolean? = null + + /** @see Executable.isVarArgs */ + var isVarArgsNot: Boolean? = null + + /** @see Executable.getParameterAnnotations */ + val parameterAnnotations = mutableListOf>() + + /** @see Executable.getParameterAnnotations */ + val parameterAnnotationsNot = mutableListOf>() + + /** @see Executable.getAnnotatedReturnType */ + val annotatedReturnType = mutableSetOf() + + /** @see Executable.getAnnotatedReturnType */ + val annotatedReturnTypeNot = mutableSetOf() + + /** @see Executable.getAnnotatedReceiverType */ + val annotatedReceiverType = mutableSetOf() + + /** @see Executable.getAnnotatedReceiverType */ + val annotatedReceiverTypeNot = mutableSetOf() + + /** @see Executable.getAnnotatedParameterTypes */ + val annotatedParameterTypes = mutableSetOf() + + /** @see Executable.getAnnotatedParameterTypes */ + val annotatedParameterTypesNot = mutableSetOf() + + /** @see Executable.getAnnotatedExceptionTypes */ + val annotatedExceptionTypes = mutableSetOf() + + /** @see Executable.getAnnotatedExceptionTypes */ + val annotatedExceptionTypesNot = mutableSetOf() + + /** @see Executable.getParameterTypes */ + open fun parameters(vararg types: Any) = apply { + this.parameters.addAll(types) + } + + /** @see Executable.getParameterTypes */ + open fun parametersNot(vararg types: Any) = apply { + this.parametersNot.addAll(types) + } + + /** @see Executable.getParameterTypes */ + open fun parameters(condition: (List>) -> Boolean) = apply { + this.parametersCondition = condition + } + + /** + * Set [parameterCount] to `0` to match methods with no parameters. + * @see Executable.getParameterCount + */ + open fun emptyParameters() = apply { + this.parameterCount = 0 + } + + /** + * Set [parameterCountCondition] to check if the method has parameters. + * @see Executable.getParameterCount + */ + open fun emptyParametersNot() = apply { + this.parameterCountCondition = { it > 0 } + } + + /** @see Executable.getTypeParameters */ + open fun typeParameters(vararg types: TypeMatcher) = apply { + this.typeParameters.addAll(types) + } + + /** @see Executable.getTypeParameters */ + open fun typeParametersNot(vararg types: TypeMatcher) = apply { + this.typeParametersNot.addAll(types) + } + + /** @see Executable.getParameterCount */ + open fun parameterCount(count: Int) = apply { + this.parameterCount = count + } + + /** @see Executable.getParameterCount */ + open fun parameterCount(condition: (Int) -> Boolean) = apply { + this.parameterCountCondition = condition + } + + /** @see Executable.getExceptionTypes */ + open fun exceptionTypes(vararg types: Any) = apply { + this.exceptionTypes.addAll(types) + } + + /** @see Executable.getExceptionTypes */ + open fun exceptionTypesNot(vararg types: Any) = apply { + this.exceptionTypesNot.addAll(types) + } + + /** @see Executable.getGenericExceptionTypes */ + open fun genericExceptionTypes(vararg types: TypeMatcher) = apply { + this.genericExceptionTypes.addAll(types) + } + + /** @see Executable.getGenericExceptionTypes */ + open fun genericExceptionTypesNot(vararg types: TypeMatcher) = apply { + this.genericExceptionTypesNot.addAll(types) + } + + /** @see Executable.getGenericParameterTypes */ + open fun genericParameters(vararg types: TypeMatcher) = apply { + this.genericParameters.addAll(types) + } + + /** @see Executable.getGenericParameterTypes */ + open fun genericParametersNot(vararg types: TypeMatcher) = apply { + this.genericParametersNot.addAll(types) + } + + /** @see Executable.isVarArgs */ + open fun isVarArgs(isVarArgs: Boolean) = apply { + this.isVarArgs = isVarArgs + } + + /** @see Executable.isVarArgs */ + open fun isVarArgsNot(isVarArgs: Boolean) = apply { + this.isVarArgsNot = isVarArgs + } + + /** @see Executable.getParameterAnnotations */ + open fun parameterAnnotations(vararg annotations: Set) = apply { + this.parameterAnnotations.addAll(annotations) + } + + /** @see Executable.getParameterAnnotations */ + open fun parameterAnnotationsNot(vararg annotations: Set) = apply { + this.parameterAnnotationsNot.addAll(annotations) + } + + /** @see Executable.getAnnotatedReturnType */ + open fun annotatedReturnType(vararg types: Any) = apply { + this.annotatedReturnType.addAll(types.toList()) + } + + /** @see Executable.getAnnotatedReturnType */ + open fun annotatedReturnTypeNot(vararg types: Any) = apply { + this.annotatedReturnTypeNot.addAll(types.toList()) + } + + /** @see Executable.getAnnotatedReceiverType */ + open fun annotatedReceiverType(vararg types: Any) = apply { + this.annotatedReceiverType.addAll(types.toList()) + } + + /** @see Executable.getAnnotatedReceiverType */ + open fun annotatedReceiverTypeNot(vararg types: Any) = apply { + this.annotatedReceiverTypeNot.addAll(types.toList()) + } + + /** @see Executable.getAnnotatedParameterTypes */ + open fun annotatedParameterTypes(vararg types: Any) = apply { + this.annotatedParameterTypes.addAll(types.toList()) + } + + /** @see Executable.getAnnotatedParameterTypes */ + open fun annotatedParameterTypesNot(vararg types: Any) = apply { + this.annotatedParameterTypesNot.addAll(types.toList()) + } + + /** @see Executable.getAnnotatedExceptionTypes */ + open fun annotatedExceptionTypes(vararg types: Any) = apply { + this.annotatedExceptionTypes.addAll(types.toList()) + } + + /** @see Executable.getAnnotatedExceptionTypes */ + open fun annotatedExceptionTypesNot(vararg types: Any) = apply { + this.annotatedExceptionTypesNot.addAll(types.toList()) + } + + override fun initializeCopiedData(newSelf: MemberCondition) { + super.initializeCopiedData(newSelf) + + (newSelf as? ExecutableCondition)?.also { + it.parameters.addAll(parameters) + it.parametersNot.addAll(parametersNot) + it.parametersCondition = parametersCondition + it.parameterCount = parameterCount + it.parameterCountCondition = parameterCountCondition + it.typeParameters.addAll(typeParameters) + it.typeParametersNot.addAll(typeParametersNot) + it.exceptionTypes.addAll(exceptionTypes) + it.exceptionTypesNot.addAll(exceptionTypesNot) + it.genericExceptionTypes.addAll(genericExceptionTypes) + it.genericExceptionTypesNot.addAll(genericExceptionTypesNot) + it.genericParameters.addAll(genericParameters) + it.genericParametersNot.addAll(genericParametersNot) + it.isVarArgs = isVarArgs + it.isVarArgsNot = isVarArgsNot + it.parameterAnnotations.addAll(parameterAnnotations) + it.parameterAnnotationsNot.addAll(parameterAnnotationsNot) + it.annotatedReturnType.addAll(annotatedReturnType) + it.annotatedReturnTypeNot.addAll(annotatedReturnTypeNot) + it.annotatedReceiverType.addAll(annotatedReceiverType) + it.annotatedReceiverTypeNot.addAll(annotatedReceiverTypeNot) + it.annotatedParameterTypes.addAll(annotatedParameterTypes) + it.annotatedParameterTypesNot.addAll(annotatedParameterTypesNot) + it.annotatedExceptionTypes.addAll(annotatedExceptionTypes) + it.annotatedExceptionTypesNot.addAll(annotatedExceptionTypesNot) + } + } + + override fun initializeMergedData(other: MemberCondition) { + super.initializeMergedData(other) + + (other as? ExecutableCondition)?.also { condition -> + condition.parameters.takeIf { it.isNotEmpty() }?.let { + parameters.clear() + parameters.addAll(it) + } + condition.parametersNot.takeIf { it.isNotEmpty() }?.let { + parametersNot.clear() + parametersNot.addAll(it) + } + condition.parametersCondition?.let { parametersCondition = it } + condition.parameterCount?.let { parameterCount = it } + condition.parameterCountCondition?.let { parameterCountCondition = it } + condition.typeParameters.takeIf { it.isNotEmpty() }?.let { + typeParameters.clear() + typeParameters.addAll(it) + } + condition.typeParametersNot.takeIf { it.isNotEmpty() }?.let { + typeParametersNot.clear() + typeParametersNot.addAll(it) + } + condition.exceptionTypes.takeIf { it.isNotEmpty() }?.let { + exceptionTypes.clear() + exceptionTypes.addAll(it) + } + condition.exceptionTypesNot.takeIf { it.isNotEmpty() }?.let { + exceptionTypesNot.clear() + exceptionTypesNot.addAll(it) + } + condition.genericExceptionTypes.takeIf { it.isNotEmpty() }?.let { + genericExceptionTypes.clear() + genericExceptionTypes.addAll(it) + } + condition.genericExceptionTypesNot.takeIf { it.isNotEmpty() }?.let { + genericExceptionTypesNot.clear() + genericExceptionTypesNot.addAll(it) + } + condition.genericParameters.takeIf { it.isNotEmpty() }?.let { + genericParameters.clear() + genericParameters.addAll(it) + } + condition.genericParametersNot.takeIf { it.isNotEmpty() }?.let { + genericParametersNot.clear() + genericParametersNot.addAll(it) + } + condition.isVarArgs?.let { isVarArgs = it } + condition.isVarArgsNot?.let { isVarArgsNot = it } + condition.parameterAnnotations.takeIf { it.isNotEmpty() }?.let { + parameterAnnotations.clear() + parameterAnnotations.addAll(it) + } + condition.parameterAnnotationsNot.takeIf { it.isNotEmpty() }?.let { + parameterAnnotationsNot.clear() + parameterAnnotationsNot.addAll(it) + } + condition.annotatedReturnType.takeIf { it.isNotEmpty() }?.let { + annotatedReturnType.clear() + annotatedReturnType.addAll(it) + } + condition.annotatedReturnTypeNot.takeIf { it.isNotEmpty() }?.let { + annotatedReturnTypeNot.clear() + annotatedReturnTypeNot.addAll(it) + } + condition.annotatedReceiverType.takeIf { it.isNotEmpty() }?.let { + annotatedReceiverType.clear() + annotatedReceiverType.addAll(it) + } + condition.annotatedReceiverTypeNot.takeIf { it.isNotEmpty() }?.let { + annotatedReceiverTypeNot.clear() + annotatedReceiverTypeNot.addAll(it) + } + condition.annotatedParameterTypes.takeIf { it.isNotEmpty() }?.let { + annotatedParameterTypes.clear() + annotatedParameterTypes.addAll(it) + } + condition.annotatedParameterTypesNot.takeIf { it.isNotEmpty() }?.let { + annotatedParameterTypesNot.clear() + annotatedParameterTypesNot.addAll(it) + } + condition.annotatedExceptionTypes.takeIf { it.isNotEmpty() }?.let { + annotatedExceptionTypes.clear() + annotatedExceptionTypes.addAll(it) + } + condition.annotatedExceptionTypesNot.takeIf { it.isNotEmpty() }?.let { + annotatedExceptionTypesNot.clear() + annotatedExceptionTypesNot.addAll(it) + } + } + } + + override val conditionStringMap + get() = super.conditionStringMap + mapOf( + PARAMETERS to parameters, + PARAMETERS_NOT to parametersNot, + PARAMETERS_CONDITION to parametersCondition, + PARAMETER_COUNT to parameterCount, + PARAMETER_COUNT_CONDITION to parameterCountCondition, + TYPE_PARAMETERS to typeParameters, + TYPE_PARAMETERS_NOT to typeParametersNot, + EXCEPTION_TYPES to exceptionTypes, + EXCEPTION_TYPES_NOT to exceptionTypesNot, + GENERIC_EXCEPTION_TYPES to genericExceptionTypes, + GENERIC_EXCEPTION_TYPES_NOT to genericExceptionTypesNot, + GENERIC_PARAMETERS to genericParameters, + GENERIC_PARAMETERS_NOT to genericParametersNot, + IS_VAR_ARGS to isVarArgs, + IS_VAR_ARGS_NOT to isVarArgsNot, + PARAMETER_ANNOTATIONS to parameterAnnotations, + PARAMETER_ANNOTATIONS_NOT to parameterAnnotationsNot, + ANNOTATED_RETURN_TYPE to annotatedReturnType, + ANNOTATED_RETURN_TYPE_NOT to annotatedReturnTypeNot, + ANNOTATED_RECEIVER_TYPE to annotatedReceiverType, + ANNOTATED_RECEIVER_TYPE_NOT to annotatedReceiverTypeNot, + ANNOTATED_PARAMETER_TYPES to annotatedParameterTypes, + ANNOTATED_PARAMETER_TYPES_NOT to annotatedParameterTypesNot, + ANNOTATED_EXCEPTION_TYPES to annotatedExceptionTypes, + ANNOTATED_EXCEPTION_TYPES_NOT to annotatedExceptionTypesNot + ) + + companion object { + const val PARAMETERS = "parameters" + const val PARAMETERS_NOT = "parametersNot" + const val PARAMETERS_CONDITION = "parametersCondition" + const val PARAMETER_COUNT = "parameterCount" + const val PARAMETER_COUNT_CONDITION = "parameterCountCondition" + const val TYPE_PARAMETERS = "typeParameters" + const val TYPE_PARAMETERS_NOT = "typeParametersNot" + const val EXCEPTION_TYPES = "exceptionTypes" + const val EXCEPTION_TYPES_NOT = "exceptionTypesNot" + const val GENERIC_EXCEPTION_TYPES = "genericExceptionTypes" + const val GENERIC_EXCEPTION_TYPES_NOT = "genericExceptionTypesNot" + const val GENERIC_PARAMETERS = "genericParameters" + const val GENERIC_PARAMETERS_NOT = "genericParametersNot" + const val IS_VAR_ARGS = "isVarArgs" + const val IS_VAR_ARGS_NOT = "isVarArgsNot" + const val PARAMETER_ANNOTATIONS = "parameterAnnotations" + const val PARAMETER_ANNOTATIONS_NOT = "parameterAnnotationsNot" + const val ANNOTATED_RETURN_TYPE = "annotatedReturnType" + const val ANNOTATED_RETURN_TYPE_NOT = "annotatedReturnTypeNot" + const val ANNOTATED_RECEIVER_TYPE = "annotatedReceiverType" + const val ANNOTATED_RECEIVER_TYPE_NOT = "annotatedReceiverTypeNot" + const val ANNOTATED_PARAMETER_TYPES = "annotatedParameterTypes" + const val ANNOTATED_PARAMETER_TYPES_NOT = "annotatedParameterTypesNot" + const val ANNOTATED_EXCEPTION_TYPES = "annotatedExceptionTypes" + const val ANNOTATED_EXCEPTION_TYPES_NOT = "annotatedExceptionTypesNot" + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/base/MemberCondition.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/base/MemberCondition.kt new file mode 100644 index 0000000..e1cee37 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/base/MemberCondition.kt @@ -0,0 +1,307 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +@file:Suppress("DuplicatedCode") + +package com.highcapable.kavaref.condition.base + +import com.highcapable.kavaref.condition.base.MemberCondition.Configuration.Optional +import com.highcapable.kavaref.condition.type.Modifiers +import com.highcapable.kavaref.resolver.base.MemberResolver +import com.highcapable.kavaref.resolver.processor.MemberProcessor +import java.lang.reflect.AnnotatedElement +import java.lang.reflect.Executable +import java.lang.reflect.Field +import java.lang.reflect.Member + +/** + * Base class for defining conditions on members [M], [R]. + * + * [T] to specify the declaring class type of the member. + */ +abstract class MemberCondition, T : Any> { + + /** + * Configure initial conditions. + * @param declaringClass the class that declares the member. + * @param memberInstance the instance of the resolved member, default is null. + * @param processorResolver the resolver for processing members, if set, + * it will be used to resolve members instead of the default resolver. + * If you want to change the global processor resolver, you can set it using [MemberProcessor.globalResolver]. + * @param superclass the superclass mode, which means that when the condition cannot find the corresponding member, + * it will search the [declaringClass]'s superclass, default is false. + * @param optional the optional mode, which means that when the condition cannot find the corresponding member, + * do not throw an exception or do not print any logs, but return an empty list, default is [Optional.NO]. + */ + data class Configuration( + val declaringClass: Class, + val memberInstance: T? = null, + var processorResolver: MemberProcessor.Resolver? = null, + var superclass: Boolean = false, + var optional: Optional = Optional.NO + ) { + + /** + * Optional mode for handling member resolution. + */ + enum class Optional { + /** Do not use optional mode. */ + NO, + + /** + * Enable optional mode to minimize exception prompts (do not throw + * exceptions), but the warning log will still be printed. + */ + NOTICE, + + /** + * Enable optional mode to minimize exception prompts (do not throw + * exceptions) and do not print any logs. + */ + SILENT + } + + companion object { + + /** + * Create a new instance of [Configuration]. + * @return [Configuration] + */ + @JvmStatic + @JvmOverloads + fun Class.createConfiguration( + memberInstance: T? = null, + processorResolver: MemberProcessor.Resolver? = null, + superclass: Boolean = false, + optional: Optional = Optional.NO + ) = Configuration(declaringClass = this, memberInstance, processorResolver, superclass, optional) + } + } + + /** + * The configuration of this condition, + * user can only set by [build] function. + */ + @get:JvmSynthetic + @set:JvmSynthetic + internal var configuration: Configuration? = null + + /** @see Member.getName */ + var name: String? = null + + /** @see Member.getName */ + var nameCondition: ((String) -> Boolean)? = null + + /** @see Member.getModifiers */ + val modifiers = mutableSetOf() + + /** @see Member.getModifiers */ + val modifiersNot = mutableSetOf() + + /** @see Member.getModifiers */ + var modifiersCondition: ((Set) -> Boolean)? = null + + /** @see Member.isSynthetic */ + var isSynthetic: Boolean? = null + + /** @see Member.isSynthetic */ + var isSyntheticNot: Boolean? = null + + /** @see AnnotatedElement.getDeclaredAnnotations */ + val annotations = mutableSetOf() + + /** @see AnnotatedElement.getDeclaredAnnotations */ + val annotationsNot = mutableSetOf() + + /** + * @see Executable.toGenericString + * @see Field.toGenericString + */ + var genericString: String? = null + + /** @see Member.getName */ + open fun name(name: String) = apply { + this.name = name + } + + /** @see Member.getName */ + open fun name(condition: (String) -> Boolean) = apply { + this.nameCondition = condition + } + + /** @see Member.getModifiers */ + open fun modifiers(vararg modifiers: Modifiers) = apply { + this.modifiers.addAll(modifiers) + } + + /** @see Member.getModifiers */ + open fun modifiersNot(vararg modifiers: Modifiers) = apply { + this.modifiersNot.addAll(modifiers) + } + + /** @see Member.getModifiers */ + open fun modifiers(condition: (Set) -> Boolean) = apply { + this.modifiersCondition = condition + } + + /** @see Member.isSynthetic */ + open fun isSynthetic(isSynthetic: Boolean) = apply { + this.isSynthetic = isSynthetic + } + + /** @see Member.isSynthetic */ + open fun isSyntheticNot(isSynthetic: Boolean) = apply { + this.isSyntheticNot = isSynthetic + } + + /** @see AnnotatedElement.getDeclaredAnnotations */ + open fun annotations(vararg annotations: Any) = apply { + this.annotations.addAll(annotations.toList()) + } + + /** @see AnnotatedElement.getDeclaredAnnotations */ + open fun annotationsNot(vararg annotations: Any) = apply { + this.annotationsNot.addAll(annotations.toList()) + } + + /** + * @see Executable.toGenericString + * @see Field.toGenericString + */ + open fun genericString(genericString: String) = apply { + this.genericString = genericString + } + + /** + * Enable superclass mode. + * @see Configuration.superclass + */ + open fun superclass() = apply { + configuration?.superclass = true + } + + /** + * Initialize the copied data from this condition to the new condition. + * @param newSelf the new condition instance. + */ + protected open fun initializeCopiedData(newSelf: MemberCondition) { + newSelf.name = name + newSelf.nameCondition = nameCondition + newSelf.modifiers.addAll(modifiers) + newSelf.modifiersNot.addAll(modifiersNot) + newSelf.isSynthetic = isSynthetic + newSelf.isSyntheticNot = isSyntheticNot + newSelf.annotations.addAll(annotations) + newSelf.annotationsNot.addAll(annotationsNot) + newSelf.genericString = genericString + } + + /** + * Initialize the merged data from this condition to the new condition. + * @param other the other condition instance to merge with. + */ + @JvmSynthetic + internal open fun initializeMergedData(other: MemberCondition) { + other.name?.let { name = it } + other.nameCondition?.let { nameCondition = it } + other.modifiers.takeIf { it.isNotEmpty() }?.let { + modifiers.clear() + modifiers.addAll(it) + } + other.modifiersNot.takeIf { it.isNotEmpty() }?.let { + modifiersNot.clear() + modifiersNot.addAll(it) + } + other.isSynthetic?.let { isSynthetic = it } + other.isSyntheticNot?.let { isSyntheticNot = it } + other.annotations.takeIf { it.isNotEmpty() }?.let { + annotations.clear() + annotations.addAll(it) + } + other.annotationsNot.takeIf { it.isNotEmpty() }?.let { + annotationsNot.clear() + annotationsNot.addAll(it) + } + other.genericString?.let { genericString = it } + } + + /** + * Create a copy of this condition. + * @return [MemberCondition]<[M], [R], [T]> + */ + abstract fun copy(): MemberCondition + + /** + * Build the condition with the given [configuration]. + * + * - Note: If you are not a manually created condition instance, then you cannot set [configuration] again. + * @param configuration the class that declares the member. + * @return [List]<[R]> + */ + @JvmOverloads + open fun build(configuration: Configuration? = null): List = + TODO("Implemented build function in subclass.") + + /** + * Get the condition string map. + * @return [Map]<[String], [Any] or null> + */ + @get:JvmSynthetic + internal open val conditionStringMap get() = mapOf( + NAME to name, + NAME_CONDITION to nameCondition, + MODIFIERS to modifiers, + MODIFIERS_NOT to modifiersNot, + MODIFIERS_CONDITION to modifiersCondition, + IS_SYNTHETIC to isSynthetic, + IS_SYNTHETIC_NOT to isSyntheticNot, + ANNOTATIONS to annotations, + ANNOTATIONS_NOT to annotationsNot, + GENERIC_STRING to genericString + ) + + companion object { + const val NAME = "name" + const val NAME_CONDITION = "nameCondition" + const val MODIFIERS = "modifiers" + const val MODIFIERS_NOT = "modifiersNot" + const val MODIFIERS_CONDITION = "modifiersCondition" + const val IS_SYNTHETIC = "isSynthetic" + const val IS_SYNTHETIC_NOT = "isSyntheticNot" + const val ANNOTATIONS = "annotations" + const val ANNOTATIONS_NOT = "annotationsNot" + const val GENERIC_STRING = "genericString" + } + + /** + * Check if the [configuration] is null and set it. + * If the [configuration] is not null, throw an exception. + * @param configuration the configuration to set. + * @throws IllegalStateException if the [configuration] is not null. + */ + protected fun checkAndSetConfiguration(configuration: Configuration) { + check(this.configuration == null) { + "Configuration already set for this condition \"$javaClass\" of \"${this.configuration}\". " + + "To prevent problems, the configuration can only be set once in a condition, " + + "otherwise use copy() to reuse the condition." + }; this.configuration = configuration + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/extension/MemberCondition.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/extension/MemberCondition.kt new file mode 100644 index 0000000..dbdbaf3 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/extension/MemberCondition.kt @@ -0,0 +1,35 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/6/20. + */ +@file:Suppress("unused") + +package com.highcapable.kavaref.condition.extension + +import com.highcapable.kavaref.condition.base.MemberCondition +import com.highcapable.kavaref.resolver.base.MemberResolver +import java.lang.reflect.Member + +/** + * Merge this condition with another [other] condition. + * @receiver the condition to merge from. + * @param other the other condition to merge with. + */ +infix fun , T : Any, U : MemberCondition> U.mergeWith(other: U) = initializeMergedData(other) \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/ClassTypeMatcher.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/ClassTypeMatcher.kt new file mode 100644 index 0000000..0a9dc8a --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/ClassTypeMatcher.kt @@ -0,0 +1,36 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/20. + */ +package com.highcapable.kavaref.condition.matcher + +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import java.lang.reflect.Type + +/** + * A [TypeMatcher] that matches a specific class type. + * @param clazz the class type to match. + */ +data class ClassTypeMatcher( + val clazz: Class<*> +) : TypeMatcher { + + override fun matches(type: Type) = type == clazz +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/GenericArrayTypeMatcher.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/GenericArrayTypeMatcher.kt new file mode 100644 index 0000000..4f5be6b --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/GenericArrayTypeMatcher.kt @@ -0,0 +1,43 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/20. + */ +package com.highcapable.kavaref.condition.matcher + +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import java.lang.reflect.GenericArrayType +import java.lang.reflect.Type + +/** + * A [TypeMatcher] that matches a generic array type. + * @see GenericArrayType.getGenericComponentType + * @see Class.getComponentType + * @param componentMatcher the [TypeMatcher] for the component type of the array. + */ +data class GenericArrayTypeMatcher( + val componentMatcher: TypeMatcher +) : TypeMatcher { + + override fun matches(type: Type) = when (type) { + is GenericArrayType -> componentMatcher.matches(type.genericComponentType) + is Class<*> -> type.isArray && componentMatcher.matches(type.componentType) + else -> false + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/ParameterizedTypeMatcher.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/ParameterizedTypeMatcher.kt new file mode 100644 index 0000000..b2a3cb9 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/ParameterizedTypeMatcher.kt @@ -0,0 +1,48 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/20. + */ +package com.highcapable.kavaref.condition.matcher + +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import com.highcapable.kavaref.condition.matcher.extension.matchesAll +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type + +/** + * A [TypeMatcher] that matches a parameterized type. + * @see ParameterizedType.getRawType + * @see ParameterizedType.getActualTypeArguments + * @param rawType the raw type of the parameterized type. + * @param arguments the [TypeMatcher]s for the type arguments of the parameterized type. + */ +data class ParameterizedTypeMatcher( + val rawType: Class<*>, + val arguments: List +) : TypeMatcher { + + override fun matches(type: Type): Boolean { + if (type !is ParameterizedType) return false + if (type.rawType != rawType) return false + val args = type.actualTypeArguments + + return args.toList().matchesAll(arguments) + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/TypeVariableMatcher.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/TypeVariableMatcher.kt new file mode 100644 index 0000000..1c31381 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/TypeVariableMatcher.kt @@ -0,0 +1,38 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/20. + */ +package com.highcapable.kavaref.condition.matcher + +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import java.lang.reflect.Type +import java.lang.reflect.TypeVariable + +/** + * A [TypeMatcher] that matches a type variable. + * @see TypeVariable.getName + * @param name the name of the type variable. + */ +data class TypeVariableMatcher( + val name: String +) : TypeMatcher { + + override fun matches(type: Type) = type is TypeVariable<*> && type.name == name +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/WildcardTypeMatcher.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/WildcardTypeMatcher.kt new file mode 100644 index 0000000..bf5df8c --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/WildcardTypeMatcher.kt @@ -0,0 +1,48 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/20. + */ +@file:Suppress("unused") + +package com.highcapable.kavaref.condition.matcher + +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import com.highcapable.kavaref.condition.matcher.extension.matchesAll +import java.lang.reflect.Type +import java.lang.reflect.WildcardType + +/** + * A [TypeMatcher] that matches a wildcard type. + * @see WildcardType.getUpperBounds + * @see WildcardType.getLowerBounds + * @param upperBounds the upper bounds of the wildcard type. + * @param lowerBounds the lower bounds of the wildcard type. + */ +data class WildcardTypeMatcher( + val upperBounds: List = emptyList(), + val lowerBounds: List = emptyList() +) : TypeMatcher { + + override fun matches(type: Type): Boolean { + if (type !is WildcardType) return false + return type.upperBounds.toList().matchesAll(upperBounds) && + type.lowerBounds.toList().matchesAll(lowerBounds) + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/base/TypeMatcher.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/base/TypeMatcher.kt new file mode 100644 index 0000000..c50d30d --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/base/TypeMatcher.kt @@ -0,0 +1,37 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/20. + */ +package com.highcapable.kavaref.condition.matcher.base + +import java.lang.reflect.Type + +/** + * Base interface for type matchers. + */ +interface TypeMatcher { + + /** + * Check if the type matches the given [type]. + * @param type the type to check. + * @return [Boolean] + */ + fun matches(type: Type): Boolean +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/extension/TypeMatcher.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/extension/TypeMatcher.kt new file mode 100644 index 0000000..528eafb --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/matcher/extension/TypeMatcher.kt @@ -0,0 +1,89 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/20. + */ +@file:Suppress("unused") + +package com.highcapable.kavaref.condition.matcher.extension + +import com.highcapable.kavaref.condition.matcher.ClassTypeMatcher +import com.highcapable.kavaref.condition.matcher.GenericArrayTypeMatcher +import com.highcapable.kavaref.condition.matcher.ParameterizedTypeMatcher +import com.highcapable.kavaref.condition.matcher.TypeVariableMatcher +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import java.lang.reflect.Type +import kotlin.reflect.KClass + +/** + * Creates a [TypeMatcher] for a specific class type. + * @param name the name of the type variable. + * @return [TypeVariableMatcher] + */ +fun typeVar(name: String) = TypeVariableMatcher(name) + +/** + * Converts a [Class] to a [ClassTypeMatcher]. + * @receiver [Class] + * @return [ClassTypeMatcher] + */ +fun Class<*>.toTypeMatcher() = ClassTypeMatcher(clazz = this) + +/** + * Converts a [KClass.java] to a [ClassTypeMatcher]. + * @receiver [KClass] + * @return [ClassTypeMatcher] + */ +fun KClass<*>.toTypeMatcher() = java.toTypeMatcher() + +/** + * Creates a [ParameterizedTypeMatcher] for a specific [Class] type with the given type arguments. + * @receiver [Class] + * @param arguments the type arguments for the parameterized type. + * @return [ParameterizedTypeMatcher] + */ +fun Class<*>.parameterizedBy(vararg arguments: TypeMatcher) = + ParameterizedTypeMatcher(rawType = this, arguments.toList()) + +/** + * Creates a [ParameterizedTypeMatcher] for a specific [KClass.java] type with the given type arguments. + * @receiver [KClass] + * @param arguments the type arguments for the parameterized type. + * @return [ParameterizedTypeMatcher] + */ +fun KClass<*>.parameterizedBy(vararg arguments: TypeMatcher) = java.parameterizedBy(*arguments) + +/** + * Creates a [GenericArrayTypeMatcher] from [TypeMatcher]. + * @receiver [TypeMatcher] + * @return [GenericArrayTypeMatcher] + */ +fun TypeMatcher.asGenericArray() = GenericArrayTypeMatcher(componentMatcher = this) + +/** + * Check if the list of [Type] matches all the given [TypeMatcher]. + * @receiver the list of [Type] to be checked. + * @param matchers the list of [TypeMatcher] to be checked. + * @return [Boolean] + */ +@JvmSynthetic +internal fun List.matchesAll(matchers: List): Boolean { + if (this.size != matchers.size) return false + return this.zip(matchers).all { (t, m) -> m.matches(t) } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/type/Modifiers.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/type/Modifiers.kt new file mode 100644 index 0000000..3a46394 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/type/Modifiers.kt @@ -0,0 +1,62 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +@file:Suppress("unused") + +package com.highcapable.kavaref.condition.type + +import java.lang.reflect.Modifier + +/** + * Modifiers for Java reflection. + * @param mod the modifier bitmask. + */ +enum class Modifiers(private val mod: Int) { + PUBLIC(Modifier.PUBLIC), + PRIVATE(Modifier.PRIVATE), + PROTECTED(Modifier.PROTECTED), + STATIC(Modifier.STATIC), + FINAL(Modifier.FINAL), + SYNCHRONIZED(Modifier.SYNCHRONIZED), + VOLATILE(Modifier.VOLATILE), + TRANSIENT(Modifier.TRANSIENT), + NATIVE(Modifier.NATIVE), + INTERFACE(Modifier.INTERFACE), + ABSTRACT(Modifier.ABSTRACT), + STRICT(Modifier.STRICT); + + /** + * Check if the modifier matches the given modifier. + * @param modifier the modifier bitmask to check against. + * @return [Boolean] + */ + fun matches(modifier: Int) = (mod and modifier) != 0 + + companion object { + + /** + * Get the set of modifiers that match the given modifier bitmask. + * @param modifier the modifier bitmask to check against. + * @return [Set]<[Modifiers]> + */ + fun matching(modifier: Int) = entries.filter { it.matches(modifier) }.toSet() + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/type/VagueType.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/type/VagueType.kt new file mode 100644 index 0000000..8c7f2d5 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/condition/type/VagueType.kt @@ -0,0 +1,69 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +package com.highcapable.kavaref.condition.type + +import com.highcapable.kavaref.extension.classOf +import kotlin.reflect.KClass + +/** + * You can fill this type in the reflection lookup condition as a placeholder. + * + * It is a vague type that you can use when the signature of the JVM type is too long and does not want to declare a type. + * + * Usage: + * + * ``` + * public void a(java.lang.String, some.name.like.too.long.Type, int); + * ``` + * + * ```kotlin + * method { + * name = "a" + * parameters(String::class, VagueType, Int::class) + * } + */ +object VagueType { + + private const val TAG = "VagueType" + + /** + * Format the placeholder to a string. + * @param placeholder the placeholder to be formatted. + * @return [String] + */ + @JvmSynthetic + internal fun format(placeholder: Any?): String? = when (placeholder) { + null -> null + is VagueType -> TAG + is Class<*> -> if (placeholder != classOf()) + placeholder.toString() + else TAG + is KClass<*> -> if (placeholder != VagueType::class) + placeholder.toString() + else TAG + is Collection<*> -> + placeholder.map { + if (it != null) format(it) else null + }.toString() + else -> placeholder.toString() + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/ConstructorResolver.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/ConstructorResolver.kt new file mode 100644 index 0000000..563d0b2 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/ConstructorResolver.kt @@ -0,0 +1,84 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +@file:Suppress("MemberVisibilityCanBePrivate") + +package com.highcapable.kavaref.resolver + +import com.highcapable.kavaref.extension.classOf +import com.highcapable.kavaref.extension.makeAccessible +import com.highcapable.kavaref.resolver.base.MemberResolver +import java.lang.reflect.Constructor + +/** + * Resolving [Constructor]. + * @param self the member to be resolved. + */ +class ConstructorResolver internal constructor(override val self: Constructor) : MemberResolver, T>(self) { + + override fun copy() = ConstructorResolver(self) + + /** + * Create a new instance of the class represented by this constructor. + * @see Constructor.newInstance + * @see createQuietly + * @see createAsType + * @see createAsTypeQuietly + * @return [T] + */ + fun create(vararg args: Any?): T { + self.makeAccessible() + return self.newInstance(*args) + } + + /** + * Create a new instance of the class represented by this constructor and cast it to the specified type [T]. + * @see Constructor.newInstance + * @see createAsTypeQuietly + * @see createQuietly + * @return [T] + */ + inline fun createAsType(vararg args: Any?): T { + self.makeAccessible() + return self.newInstance(*args) as? T ?: error("$this's instance cannot be cast to type ${classOf()}.") + } + + /** + * Create a new instance of the class represented by this constructor and ignore any exceptions. + * @see Constructor.newInstance + * @see create + * @see createAsType + * @see createAsTypeQuietly + * @return [T] or null. + */ + fun createQuietly(vararg args: Any?) = runCatching { create(*args) }.getOrNull() + + /** + * Create a new instance of the class represented by this constructor and cast it to the + * specified type [T] and ignore any exceptions. + * @see Constructor.newInstance + * @see create + * @see createAsType + * @see createQuietly + * @return [T] or null. + */ + inline fun createAsTypeQuietly(vararg args: Any?) = runCatching { createAsType(*args) }.getOrNull() +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/FieldResolver.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/FieldResolver.kt new file mode 100644 index 0000000..ba95a95 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/FieldResolver.kt @@ -0,0 +1,100 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +@file:Suppress("UNCHECKED_CAST", "MemberVisibilityCanBePrivate") + +package com.highcapable.kavaref.resolver + +import com.highcapable.kavaref.extension.makeAccessible +import com.highcapable.kavaref.resolver.base.InstanceAwareResolver +import java.lang.reflect.Field + +/** + * Resolving [Field]. + * @param self the member to be resolved. + */ +class FieldResolver internal constructor(override val self: Field) : InstanceAwareResolver(self) { + + override fun of(instance: T?) = apply { + checkAndSetInstance(instance) + } + + override fun copy() = FieldResolver(self) + + /** + * Get the value of the field. + * @see Field.get + * @see getQuietly + * @return [T] or null. + */ + @JvmName("getTyped") + fun get(): T? { + self.makeAccessible() + return self.get(instance) as? T? + } + + /** + * Get the value of the field and ignore any exceptions. + * @see Field.get + * @see get + * @return [T] or null. + */ + @JvmName("getQuietlyTyped") + fun getQuietly() = runCatching { get() }.getOrNull() + + /** + * Get the value of the field. + * @see Field.get + * @see getQuietly + * @return [Any] or null. + */ + fun get(): Any? { + self.makeAccessible() + return self.get(instance) + } + + /** + * Get the value of the field and ignore any exceptions. + * @see Field.get + * @see get + * @return [Any] or null. + */ + fun getQuietly() = runCatching { get() }.getOrNull() + + /** + * Set the value of the field. + * @see Field.set + * @see setQuietly + * @param value the value to set. + */ + fun set(value: Any?) { + self.makeAccessible() + self.set(instance, value) + } + + /** + * Set the value of the field and ignore any exceptions. + * @see Field.set + * @see set + * @param value the value to set. + */ + fun setQuietly(value: Any?) = runCatching { set(value) }.getOrNull() ?: Unit +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/MethodResolver.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/MethodResolver.kt new file mode 100644 index 0000000..8daed4a --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/MethodResolver.kt @@ -0,0 +1,81 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +@file:Suppress("UNCHECKED_CAST", "MemberVisibilityCanBePrivate") + +package com.highcapable.kavaref.resolver + +import com.highcapable.kavaref.extension.makeAccessible +import com.highcapable.kavaref.resolver.base.InstanceAwareResolver +import java.lang.reflect.Method + +/** + * Resolving [Method]. + * @param self the member to be resolved. + */ +class MethodResolver internal constructor(override val self: Method) : InstanceAwareResolver(self) { + + override fun of(instance: T?) = apply { + checkAndSetInstance(instance) + } + + override fun copy() = MethodResolver(self) + + /** + * Invoke the method with the given arguments. + * @see Method.invoke + * @see invokeQuietly + * @return [T] or null. + */ + @JvmName("invokeTyped") + fun invoke(vararg args: Any?): T? { + self.makeAccessible() + return self.invoke(instance, *args) as? T? + } + + /** + * Invoke the method with the given arguments and ignore any exceptions. + * @see Method.invoke + * @see invokeQuietly + * @return [T] or null. + */ + @JvmName("invokeQuietlyTyped") + fun invokeQuietly(vararg args: Any?) = runCatching { invoke(*args) }.getOrNull() + + /** + * Invoke the method with the given arguments. + * @see Method.invoke + * @see invoke + * @return [Any] or null. + */ + fun invoke(vararg args: Any?): Any? { + self.makeAccessible() + return self.invoke(instance, *args) + } + + /** + * Invoke the method with the given arguments and ignore any exceptions. + * @see Method.invoke + * @see invoke + * @return [Any] or null. + */ + fun invokeQuietly(vararg args: Any?) = runCatching { invoke(*args) }.getOrNull() +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/base/InstanceAwareResolver.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/base/InstanceAwareResolver.kt new file mode 100644 index 0000000..5e181a3 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/base/InstanceAwareResolver.kt @@ -0,0 +1,65 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +package com.highcapable.kavaref.resolver.base + +import com.highcapable.kavaref.condition.base.MemberCondition +import java.lang.reflect.Member + +/** + * Base [instance] aware class for resolving member [M]. + * + * [T] to specify the declaring class type of the member. + * @param self the member to be resolved. + */ +abstract class InstanceAwareResolver(override val self: M) : MemberResolver(self) { + + /** The instance of [self]. */ + @get:JvmSynthetic + @set:JvmSynthetic + internal var instance: T? = null + + /** + * Set the instance of [self]. + * + * If you have already set it in [MemberCondition.Configuration.memberInstance], + * then there is no need to set it here. + * If you want to reuse the resolver, please use [copy] to create a new resolver. + * @param instance the instance to set. + */ + abstract fun of(instance: T?): InstanceAwareResolver + + /** + * Check if the [instance] is null and set it. + * If the [instance] is not null, throw an exception. + * @param instance the instance to set. + * @throws IllegalStateException if the [instance] is not null. + */ + protected fun checkAndSetInstance(instance: T?) { + check(this.instance == null) { + "Instance already set for this resolver \"$javaClass\" of \"$self(${this.instance})\". " + + "To prevent problems, the instance object can only be set once in a resolver, " + + "otherwise use copy() to reuse the resolver." + } + + this.instance = instance + } +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/base/MemberResolver.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/base/MemberResolver.kt new file mode 100644 index 0000000..d7bb050 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/base/MemberResolver.kt @@ -0,0 +1,39 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +package com.highcapable.kavaref.resolver.base + +import java.lang.reflect.Member + +/** + * Base class for resolving member [M]. + * + * [T] to specify the declaring class type of the member. + * @param self the member to be resolved. + */ +abstract class MemberResolver(open val self: M) { + + /** + * Create a copy of this resolver. + * @return [MemberResolver]<[M]> + */ + abstract fun copy(): MemberResolver +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/processor/MemberProcessor.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/processor/MemberProcessor.kt new file mode 100644 index 0000000..793ad92 --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/resolver/processor/MemberProcessor.kt @@ -0,0 +1,518 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +@file:Suppress("UNCHECKED_CAST", "UnnecessaryVariable") + +package com.highcapable.kavaref.resolver.processor + +import com.highcapable.kavaref.condition.ConstructorCondition +import com.highcapable.kavaref.condition.FieldCondition +import com.highcapable.kavaref.condition.MethodCondition +import com.highcapable.kavaref.condition.base.ExecutableCondition +import com.highcapable.kavaref.condition.base.MemberCondition +import com.highcapable.kavaref.condition.matcher.base.TypeMatcher +import com.highcapable.kavaref.condition.type.Modifiers +import com.highcapable.kavaref.condition.type.VagueType +import com.highcapable.kavaref.extension.classOf +import com.highcapable.kavaref.extension.toClass +import com.highcapable.kavaref.extension.toClassOrNull +import com.highcapable.kavaref.generated.KavarefCoreProperties +import com.highcapable.kavaref.resolver.ConstructorResolver +import com.highcapable.kavaref.resolver.FieldResolver +import com.highcapable.kavaref.resolver.MethodResolver +import com.highcapable.kavaref.resolver.base.InstanceAwareResolver +import com.highcapable.kavaref.resolver.base.MemberResolver +import com.highcapable.kavaref.runtime.KavaRefRuntime +import java.lang.reflect.AnnotatedElement +import java.lang.reflect.Constructor +import java.lang.reflect.Executable +import java.lang.reflect.Field +import java.lang.reflect.Member +import java.lang.reflect.Method +import java.lang.reflect.Type +import kotlin.reflect.KClass + +/** + * Processing member resolver core implementation. + */ +object MemberProcessor { + + private const val PRODUCT_DESCRIPTION = "\n====== Generated by KavaRef ${KavarefCoreProperties.PROJECT_KAVAREF_CORE_VERSION} ======\n" + + /** + * Global [Resolver] used by KavaRef to resolve members. + * + * You can change this resolver to use a custom member resolver, which will affect global behavior. + */ + @JvmStatic + var globalResolver: Resolver = Resolver() + + /** + * Resolver class for resolving members. + * + * Provides methods to resolve fields, methods, and constructors of a class. + * + * You can inherit this class to implement a custom member parser, + * and override the [getDeclaredFields], [getDeclaredMethods] and + * [getDeclaredConstructors] methods to implement custom member resolution logic. + */ + open class Resolver { + + /** + * Resolve fields of the specified class [T]. + * @param declaringClass the class to resolve fields from. + * @return [List]<[Field]> + */ + open fun getDeclaredFields(declaringClass: Class): List = + runCatching { declaringClass.declaredFields.toList() }.onFailure { + KavaRefRuntime.warn("Failed to get declared fields in $this because got an exception.", it) + }.getOrNull() ?: emptyList() + + /** + * Resolve methods of the specified class [T]. + * @param declaringClass the class to resolve methods from. + * @return [List]<[Method]> + */ + open fun getDeclaredMethods(declaringClass: Class): List = + runCatching { declaringClass.declaredMethods.toList() }.onFailure { + KavaRefRuntime.warn("Failed to get declared methods in $this because got an exception.", it) + }.getOrNull() ?: emptyList() + + /** + * Resolve constructors of the specified class [T]. + * @param declaringClass the class to resolve constructors from. + * @return [List]<[Constructor]<[T]>> + */ + open fun getDeclaredConstructors(declaringClass: Class): List> = + runCatching { declaringClass.declaredConstructors.filterIsInstance>() }.onFailure { + KavaRefRuntime.warn("Failed to get declared constructors in $this because got an exception.", it) + }.getOrNull() ?: emptyList() + } + + /** + * Resolve members with current [condition] and [configuration]. + * @param condition the condition to resolve members. + * @param configuration the configuration to resolve members, must not be null. + * @return [List]<[R]> + */ + internal inline fun , T : Any> resolve( + condition: MemberCondition, + configuration: MemberCondition.Configuration? + ): List { + require(configuration != null) { + "You must provide a configuration to resolve the member use build(configuration)." + } + + return when (condition) { + is MethodCondition -> resolveInClass( + condition, configuration, configuration.declaringClass + ) { declaringClass -> + methodFilters(condition, configuration, declaringClass) + } + is ConstructorCondition -> resolveInClass( + condition, configuration, configuration.declaringClass + ) { declaringClass -> + constructorFilters(condition, configuration, declaringClass) + } + is FieldCondition -> resolveInClass( + condition, configuration, configuration.declaringClass + ) { declaringClass -> + fieldFilters(condition, configuration, declaringClass) + } + else -> error("Unsupported condition type: $condition") + } as List + } + + private fun , T : Any> resolveInClass( + condition: MemberCondition, + configuration: MemberCondition.Configuration, + declaringClass: Class<*>?, + result: (declaringClass: Class<*>) -> List + ): List { + if (declaringClass == null || declaringClass == classOf()) + return throwIfNotOptional(condition, configuration) + + return result(declaringClass).ifEmpty { + if (configuration.superclass) + resolveInClass(condition, configuration, declaringClass.superclass, result) + else throwIfNotOptional(condition, configuration) + } + } + + private fun methodFilters( + condition: MethodCondition, + configuration: MemberCondition.Configuration, + declaringClass: Class<*> + ): List> = configuration.currentProcessorResolver.getDeclaredMethods(declaringClass) + .asSequence() + .baseFilters(condition, configuration) + .executableFilters(condition, configuration) + .filter(configuration, MethodCondition.RETURN_TYPE, condition.returnType) { key, value -> + value.returnType == key.toTypeClass(configuration, noVague = "Method: returnType") + } + .filter(configuration, MethodCondition.RETURN_TYPE_CONDITION, condition.returnTypeCondition) { key, value -> + runOrElse { key(value.returnType) } + } + .filter(configuration, MethodCondition.IS_BRIDGE, condition.isBridge) { key, value -> value.isBridge == key } + .filter(configuration, MethodCondition.IS_BRIDGE_NOT, condition.isBridgeNot) { key, value -> value.isBridge != key } + .filter(configuration, MethodCondition.IS_DEFAULT, condition.isDefault) { key, value -> value.isDefault == key } + .filter(configuration, MethodCondition.IS_DEFAULT_NOT, condition.isDefaultNot) { key, value -> value.isDefault != key } + .resolve(configuration) + + private fun constructorFilters( + condition: ConstructorCondition, + configuration: MemberCondition.Configuration, + declaringClass: Class<*> + ): List> = configuration.currentProcessorResolver.getDeclaredConstructors(declaringClass) + .asSequence() + .baseFilters(condition, configuration) + .executableFilters(condition, configuration) + .resolve(configuration) + + private fun fieldFilters( + condition: FieldCondition, + configuration: MemberCondition.Configuration, + declaringClass: Class<*> + ): List> = configuration.currentProcessorResolver.getDeclaredFields(declaringClass) + .asSequence() + .baseFilters(condition, configuration) + .filter(configuration, FieldCondition.IS_ENUM_CONSTANT, condition.isEnumConstant) { key, value -> value.isEnumConstant == key } + .filter(configuration, FieldCondition.IS_ENUM_CONSTANT_NOT, condition.isEnumConstantNot) { key, value -> value.isEnumConstant != key } + .filter(configuration, FieldCondition.TYPE, condition.type) { key, value -> + value.type == key.toTypeClass(configuration, noVague = "Field: type") + } + .filter(configuration, FieldCondition.TYPE_CONDITION, condition.typeCondition) { key, value -> key(value.type) } + .filter(configuration, FieldCondition.GENERIC_TYPE, condition.genericType) { key, value -> key.matches(value.genericType) } + .filter(configuration, FieldCondition.GENERIC_TYPE_CONDITION, condition.genericTypeCondition) { key, value -> key(value.genericType) } + .resolve(configuration) + + private fun Sequence.baseFilters( + condition: MemberCondition<*, *, *>, + configuration: MemberCondition.Configuration + ) = this.filter(configuration, MemberCondition.NAME, condition.name) { key, value -> value.name == key } + .filter(configuration, MemberCondition.NAME_CONDITION, condition.nameCondition) { key, value -> runOrElse { key(value.name) } } + .filter(configuration, MemberCondition.MODIFIERS, condition.modifiers) { key, value -> + key.all { it.matches(value.modifiers) } + } + .filter(configuration, MemberCondition.MODIFIERS_NOT, condition.modifiersNot) { key, value -> + key.none { it.matches(value.modifiers) } + } + .filter(configuration, MemberCondition.MODIFIERS_CONDITION, condition.modifiersCondition) { key, value -> + runOrElse { key(Modifiers.matching(value.modifiers)) } + } + .filter(configuration, MemberCondition.IS_SYNTHETIC, condition.isSynthetic) { key, value -> value.isSynthetic == key } + .filter(configuration, MemberCondition.IS_SYNTHETIC_NOT, condition.isSyntheticNot) { key, value -> value.isSynthetic != key } + .filter(configuration, MemberCondition.ANNOTATIONS, condition.annotations) { key, value -> + val annotations = value.annotations.map { it.annotationClass.java } + compareElementTypes(key, annotations, configuration) + } + .filter(configuration, MemberCondition.ANNOTATIONS_NOT, condition.annotationsNot) { key, value -> + val annotations = value.annotations.map { it.annotationClass.java } + !compareElementTypes(key, annotations, configuration) + } + .filter(configuration, MemberCondition.GENERIC_STRING, condition.genericString) { key, value -> value.toGenericString() == key } + + private fun Sequence.executableFilters( + condition: ExecutableCondition<*, *, *>, + configuration: MemberCondition.Configuration + ) = this + .filter(configuration, ExecutableCondition.PARAMETERS, condition.parameters) { key, value -> + compareElementTypes(key, value.parameterTypes.toList(), configuration) + } + .filter(configuration, ExecutableCondition.PARAMETERS_NOT, condition.parametersNot) { key, value -> + !compareElementTypes(key, value.parameterTypes.toList(), configuration) + } + .filter(configuration, ExecutableCondition.PARAMETERS_CONDITION, condition.parametersCondition) { key, value -> + runOrElse { key(value.parameterTypes.toList()) } + } + .filter(configuration, ExecutableCondition.PARAMETER_COUNT, condition.parameterCount) { key, value -> value.parameterCount == key } + .filter(configuration, ExecutableCondition.PARAMETER_COUNT_CONDITION, condition.parameterCountCondition) { key, value -> + runOrElse { key(value.parameterCount) } + } + .filter(configuration, ExecutableCondition.TYPE_PARAMETERS, condition.typeParameters) { key, value -> + compareMatcherTypes(key, value.typeParameters.toList()) + } + .filter(configuration, ExecutableCondition.TYPE_PARAMETERS_NOT, condition.typeParametersNot) { key, value -> + !compareMatcherTypes(key, value.typeParameters.toList()) + } + .filter(configuration, ExecutableCondition.EXCEPTION_TYPES, condition.exceptionTypes) { key, value -> + compareElementTypes(key, value.exceptionTypes.toList(), configuration) + } + .filter(configuration, ExecutableCondition.EXCEPTION_TYPES_NOT, condition.exceptionTypesNot) { key, value -> + !compareElementTypes(key, value.exceptionTypes.toList(), configuration) + } + .filter(configuration, ExecutableCondition.GENERIC_EXCEPTION_TYPES, condition.genericExceptionTypes) { key, value -> + compareMatcherTypes(key, value.genericExceptionTypes.toList()) + } + .filter(configuration, ExecutableCondition.GENERIC_EXCEPTION_TYPES_NOT, condition.genericExceptionTypesNot) { key, value -> + !compareMatcherTypes(key, value.genericExceptionTypes.toList()) + } + .filter(configuration, ExecutableCondition.GENERIC_PARAMETERS, condition.genericParameters) { key, value -> + compareMatcherTypes(key, value.genericParameterTypes.toList()) + } + .filter(configuration, ExecutableCondition.GENERIC_PARAMETERS_NOT, condition.genericParametersNot) { key, value -> + !compareMatcherTypes(key, value.genericParameterTypes.toList()) + } + .filter(configuration, ExecutableCondition.IS_VAR_ARGS, condition.isVarArgs) { key, value -> value.isVarArgs == key } + .filter(configuration, ExecutableCondition.IS_VAR_ARGS_NOT, condition.isVarArgsNot) { key, value -> value.isVarArgs != key } + .filter(configuration, ExecutableCondition.PARAMETER_ANNOTATIONS, condition.parameterAnnotations) { key, value -> + val annotations = value.parameterAnnotations.map { it.map { e -> e.annotationClass.java } } + compareElementTypes(key, annotations, configuration) + } + .filter(configuration, ExecutableCondition.PARAMETER_ANNOTATIONS_NOT, condition.parameterAnnotationsNot) { key, value -> + val annotations = value.parameterAnnotations.map { it.map { e -> e.annotationClass.java } } + !compareElementTypes(key, annotations, configuration) + } + .filter(configuration, ExecutableCondition.ANNOTATED_RETURN_TYPE, condition.annotatedReturnType) { key, value -> + val annotations = value.annotatedReturnType.annotations.map { it.annotationClass.java } + compareElementTypes(key, annotations, configuration) + } + .filter(configuration, ExecutableCondition.ANNOTATED_RETURN_TYPE_NOT, condition.annotatedReturnTypeNot) { key, value -> + val annotations = value.annotatedReturnType.annotations.map { it.annotationClass.java } + !compareElementTypes(key, annotations, configuration) + } + .filter(configuration, ExecutableCondition.ANNOTATED_RECEIVER_TYPE, condition.annotatedReceiverType) { key, value -> + val annotations = value.annotatedReceiverType.annotations.map { it.annotationClass.java } + compareElementTypes(key, annotations, configuration) + } + .filter(configuration, ExecutableCondition.ANNOTATED_RECEIVER_TYPE_NOT, condition.annotatedReceiverTypeNot) { key, value -> + val annotations = value.annotatedReceiverType.annotations.map { it.annotationClass.java } + !compareElementTypes(key, annotations, configuration) + } + .filter(configuration, ExecutableCondition.ANNOTATED_PARAMETER_TYPES, condition.annotatedParameterTypes) { key, value -> + val annotations = value.annotatedParameterTypes.map { it.annotations.map { e -> e.annotationClass.java } }.flatten() + compareElementTypes(key, annotations, configuration) + } + .filter(configuration, ExecutableCondition.ANNOTATED_PARAMETER_TYPES_NOT, condition.annotatedParameterTypesNot) { key, value -> + val annotations = value.annotatedParameterTypes.map { it.annotations.map { e -> e.annotationClass.java } }.flatten() + !compareElementTypes(key, annotations, configuration) + } + .filter(configuration, ExecutableCondition.ANNOTATED_EXCEPTION_TYPES, condition.annotatedExceptionTypes) { key, value -> + val annotations = value.annotatedExceptionTypes.map { it.annotations.map { e -> e.annotationClass.java } }.flatten() + compareElementTypes(key, annotations, configuration) + } + .filter(configuration, ExecutableCondition.ANNOTATED_EXCEPTION_TYPES_NOT, condition.annotatedExceptionTypesNot) { key, value -> + val annotations = value.annotatedExceptionTypes.map { it.annotations.map { e -> e.annotationClass.java } }.flatten() + !compareElementTypes(key, annotations, configuration) + } + + private inline fun , T : Any> Sequence.resolve( + configuration: MemberCondition.Configuration + ) = when (M::class) { + Method::class -> map { MethodResolver(it as Method).apply(configuration) } + Constructor::class -> map { ConstructorResolver(it as Constructor).apply(configuration) } + Field::class -> map { FieldResolver(it as Field).apply(configuration) } + else -> error("Unsupported member type: $this") + }.toList() as List + + private fun , T : Any> throwIfNotOptional( + condition: MemberCondition, + configuration: MemberCondition.Configuration + ): List { + fun splicingString(name: String) = "No $name found matching the condition for " + + "current class${if (configuration.superclass) " (Also tried for superclass)" else ""}.\n" + + buildConditionTable(condition, configuration) + "\n" + + "Suggestion: ${if (!configuration.superclass) "Members in superclass are not reflected in the current class, " + + "you can try adding superclass() in your condition and try again. " + else "Check if the conditions are correct and valid, and try again. "}" + + val exceptionNote = "If you want to ignore this exception, adding optional() in your condition." + val message = when (condition) { + is MethodCondition -> splicingString(name = "method") + is ConstructorCondition -> splicingString(name = "constructor") + is FieldCondition -> splicingString(name = "field") + else -> error("Unsupported condition type: $condition") + } + + return if (configuration.optional == MemberCondition.Configuration.Optional.NO) throw when (condition) { + is MethodCondition -> NoSuchMethodException("$message\n$exceptionNote\n$PRODUCT_DESCRIPTION") + is ConstructorCondition -> NoSuchMethodException("$message\n$exceptionNote\n$PRODUCT_DESCRIPTION") + is FieldCondition -> NoSuchFieldException("$message\n$exceptionNote\n$PRODUCT_DESCRIPTION") + else -> error("Unsupported condition type: $condition") + } else { + if (configuration.optional == MemberCondition.Configuration.Optional.NOTICE) + KavaRefRuntime.warn(message.trim()) + emptyList() + } + } + + private inline fun MemberResolver.apply(configuration: MemberCondition.Configuration) = + apply { configuration.memberInstance?.let { if (this is InstanceAwareResolver) of(it) } } + + private fun Sequence.filter( + configuration: MemberCondition.Configuration<*>, + name: String, + key: R?, + predicate: (key: R, value: T) -> Boolean + ) = filter { condition -> + val predicateKey = if (key is Collection<*>) + key.takeIf { it.isNotEmpty() } + else key + + predicateKey?.let { + val result = predicate(it, condition) + val sKey = VagueType.format(it)?.toStringIgnore() + val sValue = condition?.toStringIgnore() + + if (configuration.optional != MemberCondition.Configuration.Optional.SILENT) + KavaRefRuntime.debug("[FILTER] [${if (result) "HIT" else "MISS"}] $name: $sKey [RESOLVED] $sValue") + + result + } ?: true + } + + private fun compareElementTypes( + conditionKey: Collection, + typesValue: List>, + configuration: MemberCondition.Configuration + ): Boolean { + // If size is different at first, return false. + if (conditionKey.size != typesValue.size) return false + + val isMatched = conditionKey + .map { it.toTypeClass(configuration) } + .filterIndexed { index, type -> + val target = typesValue[index] + type == classOf() || target == type + }.size == typesValue.size + + return isMatched + } + + @JvmName("compareElementTypesMultiple") + private fun compareElementTypes( + conditionKey: Collection>, + typesValue: List>>, + configuration: MemberCondition.Configuration + ): Boolean { + // If size is different at first, return false. + if (conditionKey.size != typesValue.size) return false + + val isMatched = conditionKey.filterIndexed { index, type -> + val target = typesValue[index] + compareElementTypes(type, target, configuration) + }.size == typesValue.size + + return isMatched + } + + private fun compareMatcherTypes( + conditionKey: Collection, + typesValue: List + ): Boolean { + // If size is different at first, return false. + if (conditionKey.size != typesValue.size) return false + + val isMatched = conditionKey.filterIndexed { index, type -> + val target = typesValue[index] + type.matches(target) + }.size == typesValue.size + + return isMatched + } + + private fun buildConditionTable( + condition: MemberCondition<*, *, *>, + configuration: MemberCondition.Configuration + ) = runCatching { + fun displayWidth(str: String) = str.sumOf { if (it.code > 127L) 2L else 1L }.toInt() + + fun padDisplay(str: String, targetWidth: Int): String { + val currentWidth = displayWidth(str) + val padding = targetWidth - currentWidth + return if (padding > 0) str + " ".repeat(padding) else str + } + + val rows = condition.conditionStringMap.mapNotNull { (label, value) -> + val displayValue = when (value) { + null -> null + is Function<*> -> "(Runtime Condition)" + is Collection<*> -> if (value.isEmpty()) null else VagueType.format(value) + else -> VagueType.format(value) + // Remove the Kotlin reflection warning message. + }?.toStringIgnore() + displayValue?.let { label to it } + } + + if (rows.isEmpty()) return configuration.declaringClass.toString() + + val originalLabelWidth = rows.maxOf { displayWidth(it.first) } + val originalValueWidth = rows.maxOf { displayWidth(it.second) } + + val headerText = configuration.declaringClass.toStringIgnore() + val headerWidth = displayWidth(headerText) + + val contentWidth = originalLabelWidth + originalValueWidth + 3 // 3: " | " + + // If header is wider, increase value column width. + val extraPadding = (headerWidth - contentWidth).coerceAtLeast(0) + + val labelWidth = originalLabelWidth + val valueWidth = originalValueWidth + extraPadding + + val border = "+-${"-".repeat(labelWidth)}-+-${"-".repeat(valueWidth)}-+" + val headerBorder = "+-${"-".repeat(labelWidth + valueWidth + 3)}-+" + val header = "| ${padDisplay(headerText, labelWidth + valueWidth + 3)} |" + + val content = rows.joinToString("\n") { (label, value) -> + "| ${padDisplay(label, labelWidth)} | ${padDisplay(value, valueWidth)} |" + } + + return listOf(headerBorder, header, border, content, border).joinToString("\n") + }.getOrDefault("${configuration.declaringClass.toStringIgnore()}\nFailed to build condition table.") + + private val MemberCondition.Configuration.currentProcessorResolver + get() = processorResolver ?: globalResolver + + private val Member.annotations get() = when (this) { + is AnnotatedElement -> declaredAnnotations + else -> error("Unsupported member type: $this") + } + + private fun Member.toGenericString() = when (this) { + is Method -> toGenericString() + is Constructor<*> -> toGenericString() + is Field -> toGenericString() + else -> error("Unsupported member type: $this") + } + + private fun Any.toTypeClass(configuration: MemberCondition.Configuration, noVague: String? = null): Class<*> { + fun Class<*>.parseVagueType() = + if (this == classOf()) + noVague?.let { error("VagueType is not supported for \"$it\".") } ?: this + else this + + return when (this) { + is Class<*> -> this + is KClass<*> -> this.java + is String -> if (configuration.optional == MemberCondition.Configuration.Optional.NO) + toClass(configuration.declaringClass.classLoader) + // If enabled optional mode, use the "Object.class" as the default return type when not found. + else toClassOrNull(configuration.declaringClass.classLoader) ?: classOf() + is VagueType -> javaClass + else -> error("Unsupported type: $this, supported types are Class, KClass, String and VagueType.") + }.parseVagueType() + } + + private fun Any.toStringIgnore() = toString().replace(" (Kotlin reflection is not available)", "") + + private inline fun runOrElse(block: () -> Boolean) = runCatching(block).getOrDefault(false) +} \ No newline at end of file diff --git a/kavaref-core/src/main/kotlin/com/highcapable/kavaref/runtime/KavaRefRuntime.kt b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/runtime/KavaRefRuntime.kt new file mode 100644 index 0000000..7ec9c9c --- /dev/null +++ b/kavaref-core/src/main/kotlin/com/highcapable/kavaref/runtime/KavaRefRuntime.kt @@ -0,0 +1,209 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/19. + */ +@file:Suppress("unused") + +package com.highcapable.kavaref.runtime + +import android.util.Log +import com.highcapable.kavaref.KavaRef +import com.highcapable.kavaref.extension.hasClass +import com.highcapable.kavaref.generated.KavarefCoreProperties +import org.slf4j.LoggerFactory + +/** + * Runtime class for KavaRef logging. + */ +object KavaRefRuntime { + + private const val TAG = KavarefCoreProperties.PROJECT_NAME + + private val slf4jLogger by lazy { LoggerFactory.getLogger(TAG) } + + private val isAndroidEnv by lazy { + javaClass.classLoader.hasClass("android.os.Build") + } + + private var logger: Logger = DefaultLogger() + + /** + * Log levels for KavaRef. + * @param levelName the name of the log level. + */ + enum class LogLevel(val levelName: String) { + /** DEBUG */ + DEBUG("debug"), + + /** INFO */ + INFO("info"), + + /** WARN */ + WARN("warn"), + + /** ERROR */ + ERROR("error"), + + /** OFF (Turn off logging) */ + OFF("off") + } + + /** + * Logger interface for KavaRef. + * + * You can implement this interface to create your own logger and set it to [KavaRef.setLogger]. + */ + interface Logger { + + /** Logger tag. */ + val tag: String + + /** + * Log a debug message. + * @param msg the message to log. + * @param throwable an optional throwable to log. + */ + fun debug(msg: Any?, throwable: Throwable? = null) + + /** + * Log an info message. + * @param msg the message to log. + * @param throwable an optional throwable to log. + */ + fun info(msg: Any?, throwable: Throwable? = null) + + /** + * Log a warning message. + * @param msg the message to log. + * @param throwable an optional throwable to log. + */ + fun warn(msg: Any?, throwable: Throwable? = null) + + /** + * Log an error message. + * @param msg the message to log. + * @param throwable an optional throwable to log. + */ + fun error(msg: Any?, throwable: Throwable? = null) + } + + /** + * Default logger implementation for KavaRef. + * + * This logger uses SLF4J for non-Android environments and Android Log for Android environments. + */ + private class DefaultLogger : Logger { + + override val tag = TAG + + override fun debug(msg: Any?, throwable: Throwable?) { + if (!isAndroidEnv) + slf4jLogger.debug(msg.toString(), throwable) + else Log.d(TAG, msg.toString(), throwable) + } + + override fun info(msg: Any?, throwable: Throwable?) { + if (!isAndroidEnv) + slf4jLogger.info(msg.toString(), throwable) + else Log.i(TAG, msg.toString(), throwable) + } + + override fun warn(msg: Any?, throwable: Throwable?) { + if (!isAndroidEnv) + slf4jLogger.warn(msg.toString(), throwable) + else Log.w(TAG, msg.toString(), throwable) + } + + override fun error(msg: Any?, throwable: Throwable?) { + if (!isAndroidEnv) + slf4jLogger.error(msg.toString(), throwable) + else Log.e(TAG, msg.toString(), throwable) + } + } + + /** + * Get or set the log level for KavaRef. + * + * Use [KavaRef.logLevel] to control it. + * @see KavaRef.logLevel + * @return [LogLevel] + */ + @get:JvmSynthetic + @set:JvmSynthetic + internal var logLevel = LogLevel.WARN + set(value) { + if (!isAndroidEnv) + // Enable level for SLF4J. + System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", value.levelName) + + field = value + } + + /** + * Set the logger for KavaRef. + * + * Use [KavaRef.setLogger] to control it. + * @see KavaRef.setLogger + * @param logger the logger to be set. + */ + @JvmSynthetic + internal fun setLogger(logger: Logger) { + this.logger = logger + } + + init { + // Initialize the log level to WARN if not set. + logLevel = logLevel + } + + /** + * DEBUG + */ + @JvmSynthetic + internal fun debug(msg: Any?, throwable: Throwable? = null) { + if (shouldLog(LogLevel.DEBUG)) logger.debug(msg, throwable) + } + + /** + * INFO + */ + @JvmSynthetic + internal fun info(msg: Any?, throwable: Throwable? = null) { + if (shouldLog(LogLevel.INFO)) logger.info(msg, throwable) + } + + /** + * WARN + */ + @JvmSynthetic + internal fun warn(msg: Any?, throwable: Throwable? = null) { + if (shouldLog(LogLevel.WARN)) logger.warn(msg, throwable) + } + + /** + * ERROR + */ + @JvmSynthetic + internal fun error(msg: Any?, throwable: Throwable? = null) { + if (shouldLog(LogLevel.ERROR)) logger.error(msg, throwable) + } + + private fun shouldLog(level: LogLevel) = logLevel.ordinal <= level.ordinal +} \ No newline at end of file diff --git a/kavaref-extension/build.gradle.kts b/kavaref-extension/build.gradle.kts new file mode 100644 index 0000000..8d28556 --- /dev/null +++ b/kavaref-extension/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + autowire(libs.plugins.kotlin.jvm) + autowire(libs.plugins.kotlin.dokka) + autowire(libs.plugins.maven.publish) +} + +group = property.project.groupName +version = property.project.kavaref.extension.version + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + withSourcesJar() +} + +kotlin { + jvmToolchain(17) + sourceSets.all { languageSettings { languageVersion = "2.0" } } + compilerOptions { + freeCompilerArgs = listOf( + "-Xno-param-assertions", + "-Xno-call-assertions", + "-Xno-receiver-assertions" + ) + } +} \ No newline at end of file diff --git a/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaArrayClass.kt b/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaArrayClass.kt new file mode 100644 index 0000000..08dc5c6 --- /dev/null +++ b/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaArrayClass.kt @@ -0,0 +1,66 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/6/5. + */ +@file:JvmName("ArrayClassUtils") +@file:Suppress("unused", "FunctionName", "UNCHECKED_CAST") + +package com.highcapable.kavaref.extension + +import java.lang.reflect.Array +import kotlin.reflect.KClass + +/** + * Create a [Class] for an array of the specified [type]. + * + * For example: + * `ArrayClass(String::class.java)` will + * return `java.lang.String[]`. + * @param type the type of the array elements. + * @return [Class]<[Array]> + */ +@JvmName("createArrayClass") +fun ArrayClass(type: Class<*>) = Array.newInstance(type, 0).javaClass as Class + +/** + * Create a [KClass.java] for an array of the specified [type]. + * + * For example: + * `ArrayClass(String::class)` will + * return `java.lang.String[]`. + * @param type the type of the array elements. + * @return [Class]<[Array]> + */ +@JvmSynthetic +fun ArrayClass(type: KClass<*>) = ArrayClass(type.java) + +/** + * Create a [Class] for an array of the specified [type]. + * + * For example: + * `ArrayClass("java.lang.String")` will + * return `java.lang.String[]`. + * @param type the class name of the array elements. + * @param loader the [ClassLoader] to load the class. + * @return [Class]<[Array]> + */ +@JvmOverloads +@JvmName("createArrayClass") +fun ArrayClass(type: String, loader: ClassLoader? = null) = ArrayClass(type.toClass(loader)) \ No newline at end of file diff --git a/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaClass.kt b/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaClass.kt new file mode 100644 index 0000000..4482697 --- /dev/null +++ b/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaClass.kt @@ -0,0 +1,671 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/31. + */ +@file:Suppress("unused", "MemberVisibilityCanBePrivate", "UNCHECKED_CAST") +@file:JvmName("ClassUtils") + +package com.highcapable.kavaref.extension + +import java.lang.reflect.Constructor +import java.lang.reflect.Modifier +import java.util.concurrent.ConcurrentHashMap +import kotlin.reflect.KClass +import kotlin.reflect.KProperty + +/** Definition [ClassLoader] Loading instance function body type. */ +private typealias ClassLoaderInitializer = () -> ClassLoader? + +/** Cache for [createInstance] function to store constructors for faster access. */ +private val createInstanceConstructorsCache = ConcurrentHashMap>() + +/** + * Provide a [ClassLoader] for reflection operations. + */ +object ClassLoaderProvider { + + /** + * The [ClassLoader] used for reflection operations. + * + * If null, it will be determined according to the default behavior of the current JVM. + */ + var classLoader: ClassLoader? = null +} + +/** + * A class that can load various classes by their names. + * @param names the names of the classes to be loaded. + */ +class VariousClass(vararg names: String) { + + private val classNames = names.toList() + + /** + * Load the first class that matches the given names using the specified [ClassLoader]. + * + * If no class is found, it throws a [NoClassDefFoundError]. + * @see loadOrNull + * @param loader the [ClassLoader] to load the class, default is [ClassLoaderProvider.classLoader]. + * @param initialize whether to initialize the class with [loader], default is false. + * @return [Class] + * @throws NoClassDefFoundError if no class is found. + */ + @JvmOverloads + fun load(loader: ClassLoader? = null, initialize: Boolean = false) = loadOrNull(loader, initialize) + ?: throw NoClassDefFoundError("VariousClass matches failed of $classNames.") + + /** + * Load the first class that matches the given names using the specified [ClassLoader]. + * @see load + * @see loadOrNull + * @return [Class]<[T]> + * @throws NoClassDefFoundError if no class is found. + * @throws IllegalStateException if the class cannot be cast to type [T]. + */ + @JvmOverloads + @JvmName("loadTyped") + fun load(loader: ClassLoader? = null, initialize: Boolean = false) = load(loader, initialize) as? Class? + ?: error("VariousClass type cast failed of $classNames.") + + /** + * Load the first class that matches the given names using the specified [ClassLoader]. + * + * If no class is found, it returns null. + * @see load + * @param loader the [ClassLoader] to load the class, default is [ClassLoaderProvider.classLoader]. + * @param initialize whether to initialize the class with [loader], default is false. + * @return [Class] or null if no class is found. + */ + @JvmOverloads + fun loadOrNull(loader: ClassLoader? = null, initialize: Boolean = false): Class<*>? { + val currentLoader = loader + ?: ClassLoaderProvider.classLoader + ?: ClassLoader.getSystemClassLoader() + + return classNames.firstOrNull { currentLoader.hasClass(it) }?.toClass(loader, initialize) + } + + /** + * Load the first class that matches the given names using the specified [ClassLoader]. + * @see load + * @see loadOrNull + * @return [Class]<[T]> or null. + */ + @JvmOverloads + @JvmName("loadOrNullTyped") + fun loadOrNull(loader: ClassLoader? = null, initialize: Boolean = false) = loadOrNull(loader, initialize) as? Class? +} + +/** + * Lazy loading [Class] instance. + * @param classDefinition the class definition. + * @param initialize whether to initialize the class with [loader]. + * @param loader the [ClassLoader] to load the class. + */ +abstract class LazyClass private constructor( + private val classDefinition: Any, + private val initialize: Boolean, + private val loader: ClassLoaderInitializer? +) { + + private var baseDefinition: Class? = null + + /** + * Get non-null instance of [Class]. + * @return [Class]<[T]> + */ + @get:JvmSynthetic + internal val nonNull get(): Class { + if (baseDefinition == null) + baseDefinition = when (classDefinition) { + is String -> classDefinition.toClass(loader?.invoke(), initialize) + is VariousClass -> classDefinition.load(loader?.invoke(), initialize) + else -> error("Unknown lazy class type \"$classDefinition\"") + } + + return baseDefinition ?: error("Exception has been thrown above.") + } + + /** + * Get nullable instance of [Class]. + * @return [Class]<[T]> or null. + */ + @get:JvmSynthetic + internal val nullable get(): Class? { + if (baseDefinition == null) + baseDefinition = when (classDefinition) { + is String -> classDefinition.toClassOrNull(loader?.invoke(), initialize) + is VariousClass -> classDefinition.loadOrNull(loader?.invoke(), initialize) + else -> error("Unknown lazy class type \"$classDefinition\".") + } + + return baseDefinition + } + + /** + * Create a non-null instance of [Class]. + * @param classDefinition the class definition, can be a [String] class name or a [VariousClass]. + * @param initialize whether to initialize the class with [loader]. + * @param loader the [ClassLoader] to load the class. + */ + class NonNull internal constructor( + classDefinition: Any, + initialize: Boolean, + loader: ClassLoaderInitializer? + ) : LazyClass(classDefinition, initialize, loader) { + + operator fun getValue(thisRef: Any?, property: KProperty<*>) = nonNull + } + + /** + * Create a nullable instance of [Class]. + * @param classDefinition the class definition, can be a [String] class name or a [VariousClass]. + * @param initialize whether to initialize the class with [loader]. + * @param loader the [ClassLoader] to load the class. + */ + class Nullable internal constructor( + classDefinition: Any, + initialize: Boolean, + loader: ClassLoaderInitializer? + ) : LazyClass(classDefinition, initialize, loader) { + + operator fun getValue(thisRef: Any?, property: KProperty<*>) = nullable + } +} + +/** + * Create a non-null instance of [Class]. + * @see lazyClassOrNull + * @param name the fully qualified class name. + * @param initialize whether to initialize the class with [loader], default is false. + * @param loader the [ClassLoader] to load the class, default is [ClassLoaderProvider.classLoader]. + * @return [LazyClass.NonNull] + */ +@JvmSynthetic +fun lazyClass(name: String, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) = + LazyClass.NonNull(name, initialize, loader) + +/** + * Create a non-null instance of [Class]. + * @see lazyClass + * @see lazyClassOrNull + * @return [LazyClass.NonNull]<[T]> + */ +@JvmSynthetic +@JvmName("lazyClassTyped") +fun lazyClass(name: String, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) = + LazyClass.NonNull(name, initialize, loader) + +/** + * Create a non-null instance of [VariousClass]. + * @see lazyClassOrNull + * @param variousClass the [VariousClass] to be loaded. + * @param initialize whether to initialize the class with [loader], default is false. + * @param loader the [ClassLoader] to load the class, default is [ClassLoaderProvider.classLoader]. + * @return [LazyClass.NonNull] + */ +@JvmSynthetic +fun lazyClass(variousClass: VariousClass, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) = + LazyClass.NonNull(variousClass, initialize, loader) + +/** + * Create a non-null instance of [VariousClass]. + * @see lazyClass + * @see lazyClassOrNull + * @return [LazyClass.NonNull]<[T]> + */ +@JvmSynthetic +@JvmName("lazyClassTyped") +fun lazyClass(variousClass: VariousClass, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) = + LazyClass.NonNull(variousClass, initialize, loader) + +/** + * Create a nullable instance of [Class]. + * @see lazyClass + * @param name the fully qualified class name. + * @param initialize whether to initialize the class with [loader], default is false. + * @param loader the [ClassLoader] to load the class, default is [ClassLoaderProvider.classLoader]. + * @return [LazyClass.Nullable] + */ +@JvmSynthetic +fun lazyClassOrNull(name: String, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) = + LazyClass.Nullable(name, initialize, loader) + +/** + * Create a nullable instance of [Class]. + * @see lazyClass + * @see lazyClassOrNull + * @return [LazyClass.Nullable]<[T]> + */ +@JvmSynthetic +@JvmName("lazyClassOrNullTyped") +fun lazyClassOrNull(name: String, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) = + LazyClass.Nullable(name, initialize, loader) + +/** + * Create a nullable instance of [VariousClass]. + * @see lazyClass + * @param variousClass the [VariousClass] to be loaded. + * @param initialize whether to initialize the class with [loader], default is false. + * @param loader the [ClassLoader] to load the class, default is [ClassLoaderProvider.classLoader]. + * @return [LazyClass.Nullable] + */ +@JvmSynthetic +fun lazyClassOrNull(variousClass: VariousClass, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) = + LazyClass.Nullable(variousClass, initialize, loader) + +/** + * Create a nullable instance of [VariousClass]. + * @see lazyClass + * @see lazyClassOrNull + * @return [LazyClass.Nullable]<[T]> + */ +@JvmSynthetic +@JvmName("lazyClassOrNullTyped") +fun lazyClassOrNull(variousClass: VariousClass, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) = + LazyClass.Nullable(variousClass, initialize, loader) + +/** + * Convert [String] class name to [Class] with [ClassLoader] and initialize. + * @see String.toClassOrNull + * @receiver the class name to be converted. + * @param loader [ClassLoader] to load the class, default is [ClassLoaderProvider.classLoader]. + * @param initialize whether to initialize the class with [loader], default is false. + * @return [Class] + */ +@JvmOverloads +@JvmName("create") +fun String.toClass(loader: ClassLoader? = null, initialize: Boolean = false): Class { + val createLoader = loader ?: ClassLoaderProvider.classLoader + + return ((if (createLoader != null) + Class.forName(this, initialize, createLoader) + else Class.forName(this)) ?: error("JVM class not resolved: $this")) as Class +} + +/** + * Convert [String] class name to [Class] with [ClassLoader] and initialize. + * @see Class.toClass + * @see String.toClassOrNull + * @return [Class]<[T]> + */ +@JvmOverloads +@JvmName("createTyped") +fun String.toClass(loader: ClassLoader? = null, initialize: Boolean = false) = + toClass(loader, initialize) as? Class? ?: error("JVM class type cast failed: $this") + +/** + * Convert [String] class name to [Class] with [ClassLoader] and initialize. + * @see String.toClassOrNull + * @receiver the class name to be converted. + * @param loader [ClassLoader] to load the class, default is [ClassLoaderProvider.classLoader]. + * @param initialize whether to initialize the class with [loader], default is false. + * @return [Class] or null if the class not found. + */ +@JvmOverloads +@JvmName("createOrNull") +fun String.toClassOrNull(loader: ClassLoader? = null, initialize: Boolean = false) = runCatching { + toClass(loader, initialize) +}.getOrNull() + +/** + * Convert [String] class name to [Class] with [ClassLoader] and initialize. + * @see Class.toClass + * @see String.toClassOrNull + * @return [Class]<[T]> or null if the class not found or type cast failed. + */ +@JvmOverloads +@JvmName("createOrNullTyped") +fun String.toClassOrNull(loader: ClassLoader? = null, initialize: Boolean = false) = + toClassOrNull(loader, initialize) as? Class? + +/** + * Create an instance of [Class] with the given arguments. + * + * - Note: If you give a null argument, it will be treated as an any type value for the constructor parameter, + * but if all arguments are null, it will throw an [IllegalStateException]. + * @see Class.createInstanceOrNull + * @see Class.createInstanceAsType + * @see Class.createInstanceAsTypeOrNull + * @receiver the [Class] to be instantiated. + * @param args the arguments to be passed to the constructor. + * @param isPublic whether to only consider public constructors, default is true. + * @return [T] + * @throws NoSuchMethodException if no suitable constructor is found. + * @throws IllegalStateException if all arguments are null. + */ +@JvmOverloads +fun Class.createInstance(vararg args: Any?, isPublic: Boolean = true): T { + fun Class<*>.wrap() = when (this) { + JBoolean.TYPE -> classOf(primitiveType = false) + JByte.TYPE -> classOf(primitiveType = false) + JCharacter.TYPE -> classOf(primitiveType = false) + JShort.TYPE -> classOf(primitiveType = false) + JInteger.TYPE -> classOf(primitiveType = false) + JLong.TYPE -> classOf(primitiveType = false) + JFloat.TYPE -> classOf(primitiveType = false) + JDouble.TYPE -> classOf(primitiveType = false) + JVoid.TYPE -> classOf(primitiveType = false) + else -> this + } + + fun filterConstructor() = declaredConstructors.asSequence() + .filter { !isPublic || it.isPublic } + .filter { it.parameterCount == args.size } + .filter { + it.parameterTypes.zip(args).all { (type, arg) -> + arg == null && !type.isPrimitive || arg?.javaClass?.isSubclassOf(type.wrap()) == true + } + }.firstOrNull()?.apply { makeAccessible() } + + fun Constructor<*>?.create() = this?.newInstance(*args) as? T? ?: throw NoSuchMethodError( + "Could not find a suitable constructor for $this with arguments: ${args.joinToString().ifBlank { "(empty)" }}." + ) + + // If all arguments are null, throw an exception. + if (args.isNotEmpty() && args.all { it == null }) + error("Not allowed to create an instance with all null arguments for $this.") + + val constructorKey = buildString { + append(name); append('|') + args.forEach { arg -> + append(arg?.javaClass?.name ?: "null") + append('|') + } + append("isPublic: $isPublic") + } + + return createInstanceConstructorsCache[constructorKey]?.create() ?: run { + val constructor = filterConstructor()?.also { + createInstanceConstructorsCache[constructorKey] = it + } + constructor.create() + } +} + +/** + * Create an instance of [KClass.java] with the given arguments. + * @see Class.createInstance + */ +@JvmSynthetic +fun KClass.createInstance(vararg args: Any?, isPublic: Boolean = true) = + java.createInstance(*args, isPublic = isPublic) + +/** + * Create an instance of [Class] with the given arguments or return null if not found. + * @see Class.createInstance + * @see Class.createInstanceAsType + * @see Class.createInstanceAsTypeOrNull + * @receiver the [Class] to be instantiated. + * @param args the arguments to be passed to the constructor. + * @param isPublic whether to only consider public constructors, default is true. + * @return [T] or null if no suitable constructor is found. + */ +@JvmOverloads +fun Class.createInstanceOrNull(vararg args: Any?, isPublic: Boolean = true) = + runCatching { createInstance(*args, isPublic = isPublic) }.getOrNull() + +/** + * Create an instance of [KClass.java] with the given arguments or return null if not found. + * @see Class.createInstanceOrNull + */ +@JvmSynthetic +fun KClass.createInstanceOrNull(vararg args: Any?, isPublic: Boolean = true) = + java.createInstanceOrNull(*args, isPublic = isPublic) + +/** + * Create an instance of [Class] with the given arguments and cast it to the specified type [T]. + * @see Class.createInstance + * @see Class.createInstanceOrNull + * @see Class.createInstanceAsTypeOrNull + * @return [T] + * @throws NoSuchMethodException if no suitable constructor is found. + * @throws IllegalStateException if the instance cannot be cast to type [T] or if all arguments are null. + */ +inline fun Class<*>.createInstanceAsType(vararg args: Any?, isPublic: Boolean = true) = + createInstance(*args, isPublic = isPublic) as? T ?: error("$this's instance cannot be cast to type ${classOf()}.") + +/** + * Create an instance of [KClass.java] with the given arguments and cast it to the specified type [T]. + * @see Class.createInstanceAsType + */ +inline fun KClass<*>.createInstanceAsType(vararg args: Any?, isPublic: Boolean = true) = + java.createInstanceAsType(*args, isPublic = isPublic) + +/** + * Create an instance of [Class] with the given arguments and cast it to the specified type [T] or return null if not found. + * @see Class.createInstance + * @see Class.createInstanceOrNull + * @see Class.createInstanceAsType + * @return [T] or null if no suitable constructor is found or the instance cannot be cast to type [T]. + */ +inline fun Class<*>.createInstanceAsTypeOrNull(vararg args: Any?, isPublic: Boolean = true) = + runCatching { createInstanceAsType(*args, isPublic = isPublic) }.getOrNull() + +/** + * Create an instance of [KClass.java] with the given arguments and cast it to the specified type [T] or return null if not found. + * @see Class.createInstanceAsTypeOrNull + */ +inline fun KClass<*>.createInstanceAsTypeOrNull(vararg args: Any?, isPublic: Boolean = true) = + java.createInstanceAsTypeOrNull(*args, isPublic = isPublic) + +/** + * Load the class with the given name using [ClassLoader] or return null if not found. + * @receiver the [ClassLoader] to be used. + * @param name the class name to be loaded. + * @return [Class] or null. + */ +fun ClassLoader.loadClassOrNull(name: String) = runCatching { loadClass(name) as? Class? }.getOrNull() + +/** + * Check if the [ClassLoader] can load the class with the given name. + * @receiver the [ClassLoader] to be checked. + * @param name the class name to be checked. + * @return [Boolean] true if the class can be loaded, false otherwise. + */ +fun ClassLoader.hasClass(name: String) = loadClassOrNull(name) != null + +/** + * Typecast [T] to [Class]. + * @param primitiveType whether to return the primitive type of [T] if it is a primitive type, default is true. + * @return [Class]<[T]> + */ +inline fun classOf(primitiveType: Boolean = true) = + if (primitiveType) T::class.javaPrimitiveType ?: T::class.java else T::class.javaObjectType + +/** + * Use [Class.isAssignableFrom] to check if [Class] is a subclass of [superclass]. + * @see Class.isNotSubclassOf + * @receiver the class to be checked. + * @param superclass the superclass to be checked. + * @return true if [Class] is a subclass of [superclass], false otherwise. + */ +infix fun Class.isSubclassOf(superclass: Class<*>) = superclass.isAssignableFrom(this) + +/** + * Use [Class.isAssignableFrom] to check if [KClass.java] is a subclass of [superclass] ([KClass.java]). + * @see Class.isSubclassOf + */ +@JvmSynthetic +infix fun KClass.isSubclassOf(superclass: KClass<*>) = java isSubclassOf superclass.java + +/** + * Use [Class.isAssignableFrom] to check if [KClass.java] is a subclass of [superclass]. + * @see Class.isSubclassOf + */ +@JvmSynthetic +infix fun KClass.isSubclassOf(superclass: Class<*>) = java isSubclassOf superclass + +/** + * Use [Class.isAssignableFrom] to check if [Class] is a subclass of [superclass] ([KClass.java]). + * @see Class.isSubclassOf + */ +@JvmSynthetic +infix fun Class.isSubclassOf(superclass: KClass<*>) = this isSubclassOf superclass.java + +/** + * Use [Class.isAssignableFrom] to check if [Class] is not a subclass of [superclass]. + * @see Class.isSubclassOf + * @receiver the class to be checked. + * @param superclass the superclass to be checked. + * @return true if [Class] is not a subclass of [superclass], false otherwise. + */ +infix fun Class.isNotSubclassOf(superclass: Class<*>) = !isSubclassOf(superclass) + +/** + * Use [Class.isAssignableFrom] to check if [KClass.java] is not a subclass of [superclass] ([KClass.java]). + * @see Class.isNotSubclassOf + */ +@JvmSynthetic +infix fun KClass.isNotSubclassOf(superclass: KClass<*>) = java isNotSubclassOf superclass.java + +/** + * Use [Class.isAssignableFrom] to check if [KClass.java] is not a subclass of [superclass]. + * @see Class.isNotSubclassOf + */ +@JvmSynthetic +infix fun KClass.isNotSubclassOf(superclass: Class<*>) = java isNotSubclassOf superclass + +/** + * Use [Class.isAssignableFrom] to check if [Class] is not a subclass of [superclass] ([KClass.java]). + * @see Class.isNotSubclassOf + */ +@JvmSynthetic +infix fun Class.isNotSubclassOf(superclass: KClass<*>) = this isNotSubclassOf superclass.java + +/** + * Whether the current [Class] has inheritance relationship, + * and the superclass is [Any] will be considered to have no inheritance relationship. + * @receiver the [Class] to be checked. + * @return [Boolean] + */ +val Class.hasSuperclass get() = superclass != null && superclass != classOf() + +/** + * Whether the current [KClass.java] has inheritance relationship, + * and the superclass is [Any] will be considered to have no inheritance relationship. + * @see Class.hasSuperclass + */ +@get:JvmSynthetic +val KClass.hasSuperclass get() = java.hasSuperclass + +/** + * Whether the current [Class] has implemented interfaces. + * @receiver the [Class] to be checked. + * @return [Boolean] + */ +val Class.hasInterfaces get() = interfaces.isNotEmpty() + +/** + * Whether the current [KClass.java] has implemented interfaces. + * @see Class.hasInterfaces + */ +@get:JvmSynthetic +val KClass.hasInterfaces get() = java.hasInterfaces + +// Class extension properties for checking modifiers. + +/** + * Check if the [Class] is public. + * @see Modifier.isPublic + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is public, `false` otherwise. + */ +val Class.isPublic get() = Modifier.isPublic(modifiers) + +/** + * Check if the [Class] is private. + * @see Modifier.isPrivate + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is private, `false` otherwise. + */ +val Class.isPrivate get() = Modifier.isPrivate(modifiers) + +/** + * Check if the [Class] is protected. + * @see Modifier.isProtected + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is protected, `false` otherwise. + */ +val Class.isProtected get() = Modifier.isProtected(modifiers) + +/** + * Check if the [Class] is static. + * @see Modifier.isStatic + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is static, `false` otherwise. + */ +val Class.isStatic get() = Modifier.isStatic(modifiers) + +/** + * Check if the [Class] is final. + * @see Modifier.isFinal + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is final, `false` otherwise. + */ +val Class.isFinal get() = Modifier.isFinal(modifiers) + +/** + * Check if the [Class] is synchronized. + * @see Modifier.isSynchronized + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is synchronized, `false` otherwise. + */ +val Class.isSynchronized get() = Modifier.isSynchronized(modifiers) + +/** + * Check if the [Class] is volatile. + * @see Modifier.isVolatile + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is volatile, `false` otherwise. + */ +val Class.isVolatile get() = Modifier.isVolatile(modifiers) + +/** + * Check if the [Class] is transient. + * @see Modifier.isTransient + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is transient, `false` otherwise. + */ +val Class.isTransient get() = Modifier.isTransient(modifiers) + +/** + * Check if the [Class] is native. + * @see Modifier.isNative + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is native, `false` otherwise. + */ +val Class.isNative get() = Modifier.isNative(modifiers) + +/** + * Check if the [Class] is abstract. + * @see Modifier.isAbstract + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is abstract, `false` otherwise. + */ +val Class.isAbstract get() = Modifier.isAbstract(modifiers) + +/** + * Check if the [Class] is strict. + * @see Modifier.isStrict + * @receiver the [Class] to be checked. + * @return `true` if the [Class] is strict, `false` otherwise. + */ +val Class.isStrict get() = Modifier.isStrict(modifiers) \ No newline at end of file diff --git a/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaMember.kt b/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaMember.kt new file mode 100644 index 0000000..5fb046b --- /dev/null +++ b/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaMember.kt @@ -0,0 +1,138 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/31. + */ +@file:Suppress("unused") +@file:JvmName("MemberUtils") + +package com.highcapable.kavaref.extension + +import java.lang.reflect.AccessibleObject +import java.lang.reflect.Member +import java.lang.reflect.Modifier + +/** + * Make [AccessibleObject] implements [Member] accessible. + * @receiver the [Member] to be made accessible. + */ +inline fun T.makeAccessible() { + (this as? AccessibleObject)?.let { + @Suppress("DEPRECATION") + if (!it.isAccessible) it.isAccessible = true + } +} + +// Member extension properties for checking modifiers. + +/** + * Check if the [Member] is public. + * @see Modifier.isPublic + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is public, `false` otherwise. + */ +val Member.isPublic get() = Modifier.isPublic(modifiers) + +/** + * Check if the [Member] is private. + * @see Modifier.isPrivate + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is private, `false` otherwise. + */ +val Member.isPrivate get() = Modifier.isPrivate(modifiers) + +/** + * Check if the [Member] is protected. + * @see Modifier.isProtected + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is protected, `false` otherwise. + */ +val Member.isProtected get() = Modifier.isProtected(modifiers) + +/** + * Check if the [Member] is static. + * @see Modifier.isStatic + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is static, `false` otherwise. + */ +val Member.isStatic get() = Modifier.isStatic(modifiers) + +/** + * Check if the [Member] is final. + * @see Modifier.isFinal + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is final, `false` otherwise. + */ +val Member.isFinal get() = Modifier.isFinal(modifiers) + +/** + * Check if the [Member] is synchronized. + * @see Modifier.isSynchronized + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is synchronized, `false` otherwise. + */ +val Member.isSynchronized get() = Modifier.isSynchronized(modifiers) + +/** + * Check if the [Member] is volatile. + * @see Modifier.isVolatile + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is volatile, `false` otherwise. + */ +val Member.isVolatile get() = Modifier.isVolatile(modifiers) + +/** + * Check if the [Member] is transient. + * @see Modifier.isTransient + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is transient, `false` otherwise. + */ +val Member.isTransient get() = Modifier.isTransient(modifiers) + +/** + * Check if the [Member] is native. + * @see Modifier.isNative + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is native, `false` otherwise. + */ +val Member.isNative get() = Modifier.isNative(modifiers) + +/** + * Check if the [Member] is an interface. + * @see Modifier.isInterface + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is an interface, `false` otherwise. + */ +val Member.isInterface get() = Modifier.isInterface(modifiers) + +/** + * Check if the [Member] is abstract. + * @see Modifier.isAbstract + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is abstract, `false` otherwise. + */ +val Member.isAbstract get() = Modifier.isAbstract(modifiers) + +/** + * Check if the [Member] is strict. + * @see Modifier.isStrict + * @receiver the [Member] to be checked. + * @return `true` if the [Member] is strict, `false` otherwise. + */ +val Member.isStrict get() = Modifier.isStrict(modifiers) \ No newline at end of file diff --git a/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaType.kt b/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaType.kt new file mode 100644 index 0000000..eb0d6e0 --- /dev/null +++ b/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaType.kt @@ -0,0 +1,106 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/6/7. + */ +@file:Suppress("unused", "UNCHECKED_CAST") +@file:JvmName("TypeUtils") + +package com.highcapable.kavaref.extension + +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type +import kotlin.reflect.KClass + +/** + * Convert [Type] to [Class]. + * @see Type.toClassOrNull + * @receiver the [Type] to be converted. + * @return [Class]<[T]> + * @throws TypeCastException if the conversion fails. + */ +@JvmName("toClassTyped") +fun Type.toClass(): Class = when (this) { + is Class<*> -> this as Class + is ParameterizedType -> rawType.toClass() + else -> throw TypeCastException("Cannot cast type $this to java.lang.Class object.") +} + +/** + * Convert [Type] to [Class]. + * @see Type.toClass + * @see Type.toClassOrNull + * @return [Class] + */ +fun Type.toClass() = toClass() + +/** + * Safely convert [Type] to [Class] or return null if it fails. + * @see Type.toClass + * @receiver the [Type] to be converted. + * @return [Class]<[T]> or null. + */ +@JvmName("toClassTypedOrNull") +fun Type.toClassOrNull() = runCatching { toClass() }.getOrNull() + +/** + * Safely convert [Type] to [Class] or return null if it fails. + * @see Type.toClass + * @see Type.toClassOrNull + * @return [Class] or null. + */ +fun Type.toClassOrNull() = toClassOrNull() + +/** + * Convert [Type] to [ParameterizedType]. + * @see Type.asParameterizedTypeOrNull + * @receiver the [Class] to get the [ParameterizedType]. + * @return [ParameterizedType] + */ +inline fun T.asParameterizedType() = this as ParameterizedType + +/** + * Safely convert [Type] to [ParameterizedType] or return null if it fails. + * @see Type.asParameterizedType + * @receiver the [Class] to get the [ParameterizedType]. + * @return [ParameterizedType] or null. + */ +inline fun T.asParameterizedTypeOrNull() = this as? ParameterizedType? + +/** + * Get the type arguments of the superclass of this [Class] or return an empty array if it fails. + * + * This function will implement the following functions: + * + * ```kotlin + * (Class.genericSuperclass as ParameterizedType).actualTypeArguments + * ``` + * @receiver the [Class] to get the type arguments of its superclass. + * @return [Array]<[Type]> + */ +fun Class.genericSuperclassTypeArguments(): Array = runCatching { + genericSuperclass.asParameterizedTypeOrNull()?.actualTypeArguments ?: emptyArray() +}.getOrDefault(emptyArray()) + +/** + * Get the type arguments of the superclass of this [KClass.java] or return an empty array if it fails. + * @see Class.genericSuperclassTypeArguments + */ +@JvmSynthetic +fun KClass.genericSuperclassTypeArguments() = java.genericSuperclassTypeArguments() \ No newline at end of file diff --git a/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaWrapperClasses.kt b/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaWrapperClasses.kt new file mode 100644 index 0000000..ecc6794 --- /dev/null +++ b/kavaref-extension/src/main/kotlin/com/highcapable/kavaref/extension/JavaWrapperClasses.kt @@ -0,0 +1,52 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/25. + */ +@file:Suppress("unused", "PLATFORM_CLASS_MAPPED_TO_KOTLIN", "RemoveRedundantQualifierName") +@file:JvmName("JavaWrapperClassesUtils") + +package com.highcapable.kavaref.extension + +/** Reference to Java wrapper class [java.lang.Boolean]. */ +typealias JBoolean = java.lang.Boolean + +/** Reference to Java wrapper class [java.lang.Byte]. */ +typealias JByte = java.lang.Byte + +/** Reference to Java wrapper class [java.lang.Character]. */ +typealias JCharacter = java.lang.Character + +/** Reference to Java wrapper class [java.lang.Double]. */ +typealias JDouble = java.lang.Double + +/** Reference to Java wrapper class [java.lang.Float]. */ +typealias JFloat = java.lang.Float + +/** Reference to Java wrapper class [java.lang.Integer]. */ +typealias JInteger = java.lang.Integer + +/** Reference to Java wrapper class [java.lang.Long]. */ +typealias JLong = java.lang.Long + +/** Reference to Java wrapper class [java.lang.Short]. */ +typealias JShort = java.lang.Short + +/** Reference to Java wrapper class [java.lang.Void]. */ +typealias JVoid = java.lang.Void \ No newline at end of file diff --git a/samples/kavaref-demo/build.gradle.kts b/samples/kavaref-demo/build.gradle.kts new file mode 100644 index 0000000..f51a15f --- /dev/null +++ b/samples/kavaref-demo/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + autowire(libs.plugins.kotlin.jvm) +} + +group = property.project.samples.kavaref.demo.groupName +version = property.project.samples.kavaref.demo.version + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +kotlin { + jvmToolchain(17) + sourceSets.all { languageSettings { languageVersion = "2.0" } } + compilerOptions { + freeCompilerArgs = listOf( + "-Xno-param-assertions", + "-Xno-call-assertions", + "-Xno-receiver-assertions" + ) + } +} + +dependencies { + implementation(projects.kavarefCore) + implementation(projects.kavarefExtension) +} \ No newline at end of file diff --git a/samples/kavaref-demo/src/main/java/com/highcapable/kavaref/demo/Main.java b/samples/kavaref-demo/src/main/java/com/highcapable/kavaref/demo/Main.java new file mode 100644 index 0000000..2fa5395 --- /dev/null +++ b/samples/kavaref-demo/src/main/java/com/highcapable/kavaref/demo/Main.java @@ -0,0 +1,68 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/16. + */ +package com.highcapable.kavaref.demo; + +import com.highcapable.kavaref.KavaRef; +import com.highcapable.kavaref.demo.test.Test; +import com.highcapable.kavaref.runtime.KavaRefRuntime; + +public class Main { + + public static void main(String[] args) { + // Enable KavaRef debug level logging. + KavaRef.setLogLevel(KavaRefRuntime.LogLevel.DEBUG); + // Resolve the Test class using KavaRef. + var memberScope = KavaRef.resolveClass(Test.class); + // Create an instance of the Test class using the resolved class. + var test = memberScope.constructor() + .emptyParameters() + .build() + .get(0) + .create(); + // Invoke a method, modify a field, and retrieve a value using the resolved class. + memberScope.method() + .name("test") + .parameters(String.class) + .build() + .get(0) + .of(test) + .invoke("reflection test"); + // Modify the field 'myTest' and retrieve its value using the resolved class. + memberScope.field() + .name("myTest") + .type(String.class) + .build() + .get(0) + .of(test) + .set("Hello modified reflection test"); + // Retrieve the value of the field 'myTest' using the resolved class. + var testString = (String) memberScope.method() + .name("getTest") + .emptyParameters() + .build() + .get(0) + .of(test) + .invoke(); + // Print the value of the field 'myTest'. + System.out.println(testString); + } +} diff --git a/samples/kavaref-demo/src/main/java/com/highcapable/kavaref/demo/test/Test.java b/samples/kavaref-demo/src/main/java/com/highcapable/kavaref/demo/test/Test.java new file mode 100644 index 0000000..01f6c86 --- /dev/null +++ b/samples/kavaref-demo/src/main/java/com/highcapable/kavaref/demo/test/Test.java @@ -0,0 +1,44 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/24. + */ +package com.highcapable.kavaref.demo.test; + +@SuppressWarnings("ALL") +public class Test { + + private String myTest = "Hello private Test"; + + private Test() { + System.out.println("Create private Test"); + } + + private void test(String myTest) { + System.out.println("Hello private " + myTest); + } + + public Test(String aa,Boolean bb,boolean cc){ + System.out.println("Create Test with parameters: " + aa + ", " + bb + ", " + cc); + } + + private String getTest() { + return this.myTest; + } +} diff --git a/samples/kavaref-demo/src/main/kotlin/com/highcapable/kavaref/demo/Main.kt b/samples/kavaref-demo/src/main/kotlin/com/highcapable/kavaref/demo/Main.kt new file mode 100644 index 0000000..21c3e24 --- /dev/null +++ b/samples/kavaref-demo/src/main/kotlin/com/highcapable/kavaref/demo/Main.kt @@ -0,0 +1,80 @@ +/* + * KavaRef - A modernizing Java Reflection with Kotlin. + * Copyright (C) 2019 HighCapable + * https://github.com/HighCapable/KavaRef + * + * Apache License Version 2.0 + * + * 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 + * + * https://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. + * + * This file is created by fankes on 2025/5/10. + */ +package com.highcapable.kavaref.demo + +import com.highcapable.kavaref.KavaRef +import com.highcapable.kavaref.KavaRef.Companion.resolve +import com.highcapable.kavaref.demo.test.Test +import com.highcapable.kavaref.runtime.KavaRefRuntime + +fun main() { + // Enable KavaRef debug level logging. + KavaRef.logLevel = KavaRefRuntime.LogLevel.DEBUG + // Resolve the Test class using KavaRef. + // Create an instance of the Test class using the resolved class. + val test = Test::class.resolve() + .firstConstructor { + emptyParameters() + }.create() + // Invoke a method, modify a field, and retrieve a value using the resolved class. + // (1) Call from Class. + Test::class.resolve() + .firstMethod { + name = "test" + parameters(String::class) + }.of(test).invoke("reflection test") + // (2) Call from Object. + test.resolve() + .firstMethod { + name = "test" + parameters(String::class) + }.invoke("reflection test") + // Modify the field 'myTest' and retrieve its value using the resolved class. + // (1) Call from Class. + Test::class.resolve() + .firstField { + name = "myTest" + type = String::class + }.of(test).set("Hello modified reflection test") + // (2) Call from Object. + test.resolve() + .firstField { + name = "myTest" + type = String::class + }.set("Hello modified reflection test") + // Retrieve the value of the field 'myTest' using the resolved class. + // (1) Call from Class. + val testString1 = Test::class.resolve() + .firstMethod { + name = "getTest" + emptyParameters() + }.of(test).invoke() + // (2) Call from Object. + val testString2 = test.resolve() + .firstMethod { + name = "getTest" + emptyParameters() + }.invoke() + // Print the value of the field 'myTest'. + println(testString1) + println(testString2) +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..829fe91 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,30 @@ +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +plugins { + id("com.highcapable.sweetdependency") version "1.0.4" + id("com.highcapable.sweetproperty") version "1.0.5" +} +sweetDependency { + isEnableVerboseMode = false +} +sweetProperty { + global { + sourcesCode { + includeKeys( + "^project\\..*\$".toRegex(), + "^gradle\\..*\$".toRegex() + ) + isEnableRestrictedAccess = true + } + } + rootProject { all { isEnable = false } } +} +rootProject.name = "KavaRef" +include(":kavaref-core", ":kavaref-extension", "kavaref-android-stub") +include(":samples:kavaref-demo") \ No newline at end of file