Initial commit

This commit is contained in:
2025-06-25 19:05:35 +08:00
commit e949662e7c
104 changed files with 11697 additions and 0 deletions

35
.editorconfig Normal file
View File

@@ -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

110
.gitignore vendored Normal file
View File

@@ -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

8
.idea/.gitignore generated vendored Normal file
View File

@@ -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

View File

@@ -0,0 +1 @@
2019 HighCapable

View File

@@ -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.

View File

@@ -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.
*/

View File

@@ -0,0 +1 @@
A modernizing Java Reflection with Kotlin.

View File

@@ -0,0 +1 @@
KavaRef

View File

@@ -0,0 +1 @@
https://github.com/HighCapable/KavaRef

View File

@@ -0,0 +1,6 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
annotation class ${NAME}

View File

@@ -0,0 +1,7 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
class ${NAME} {
}

View File

@@ -0,0 +1,6 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
data class ${NAME}()

View File

@@ -0,0 +1,7 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
enum class ${NAME} {
}

View File

@@ -0,0 +1,5 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end

View File

@@ -0,0 +1,7 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
interface ${NAME} {
}

View File

@@ -0,0 +1,7 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
object ${NAME} {
}

11
.idea/icon.svg generated Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,10 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

6
.idea/kotlinc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.1.21" />
</component>
</project>

7
.idea/ktlint-plugin.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.nbadal.ktlint.KtlintProjectSettings">
<ktlintMode>MANUAL</ktlintMode>
<ktlintRulesetVersion>DEFAULT</ktlintRulesetVersion>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

202
LICENSE Normal file
View File

@@ -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.

72
README-zh-CN.md Normal file
View File

@@ -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)
<img src="img-src/icon.svg" width = "100" height = "100" alt="LOGO"/>
一个使用 Kotlin 实现的现代化 Java 反射。
[English](README.md) | 简体中文
| <img src="https://github.com/HighCapable/.github/blob/main/img-src/logo.jpg?raw=true" width = "30" height = "30" alt="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) 前往文档页面查看更多详细教程和内容。
## 项目推广
<!--suppress HtmlDeprecatedAttribute -->
<div align="center">
<h2>嘿,还请君留步!👋</h2>
<h3>这里有 Android 开发工具、UI 设计、Gradle 插件、Xposed 模块和实用软件等相关项目。</h3>
<h3>如果下方的项目能为你提供帮助,不妨为我点个 star 吧!</h3>
<h3>所有项目免费、开源,遵循对应开源许可协议。</h3>
<h1><a href="https://github.com/fankes/fankes/blob/main/project-promote/README-zh-CN.md">→ 查看更多关于我的项目,请点击这里 ←</a></h1>
</div>
## 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

75
README.md Normal file
View File

@@ -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)
<img src="img-src/icon.svg" width = "100" height = "100" alt="LOGO"/>
A modernizing Java Reflection with Kotlin.
English | [简体中文](README-zh-CN.md)
| <img src="https://github.com/HighCapable/.github/blob/main/img-src/logo.jpg?raw=true" width = "30" height = "30" alt="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
<!--suppress HtmlDeprecatedAttribute -->
<div align="center">
<h2>Hey, please stay! 👋</h2>
<h3>Here are related projects such as Android development tools, UI design, Gradle plugins, Xposed Modules and practical software. </h3>
<h3>If the project below can help you, please give me a star! </h3>
<h3>All projects are free, open source, and follow the corresponding open source license agreement. </h3>
<h1><a href="https://github.com/fankes/fankes/blob/main/project-promote/README.md">→ To see more about my projects, please click here ←</a></h1>
</div>
## 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

57
build.gradle.kts Normal file
View File

@@ -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<PublishingExtension> {
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<DokkaTask>().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<in Project>) {
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"
}

4
docs-source/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/node_modules
/src/.vuepress/.cache
/src/.vuepress/.temp
/dist

3
docs-source/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"git.ignoreLimitWarning": true
}

2
docs-source/build-dokka.sh Executable file
View File

@@ -0,0 +1,2 @@
cd ..
./gradlew :kavaref-core:publishKDoc :kavaref-extension:publishKDoc

17
docs-source/package.json Normal file
View File

@@ -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": {}
}

View File

@@ -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: '搜索' }
}
})
]
};

View File

@@ -0,0 +1,148 @@
import { i18n } from './utils';
interface PageLinkRefs {
dev: Record<string, string>[];
prod: Record<string, string>[];
}
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')
}]
};

View File

@@ -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<string, string>[]) => {
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);
};
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -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};
}
}

View File

@@ -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

View File

@@ -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 &ensp;<Badge type="tip" text="latest" vertical="middle" />
- The first version is submitted to Maven
## kavaref-extension
### 1.0.0 | 2025.06.25 &ensp;<Badge type="tip" text="latest" vertical="middle" />
- The first version is submitted to Maven

View File

@@ -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.

View File

@@ -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<String>()
// Call myMethod1.
val methodValue = firstMethod { name = "myMethod1" }.invoke<String>("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.
:::

View File

@@ -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
<div style="display: flex; gap: 16px;">
<div style="flex: 1;">
<h4>KavaRef</h4>
```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!")
```
</div>
<div style="flex: 1;">
<h4>YukiReflection</h4>
```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!")
```
</div>
</div>
`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<MemberResolver>`.
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<String>()`, `get<Int>()`, `invoke<String>(...)`, `invoke<Int>(...)`, 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<MyClass>
.get<String>() // 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<String>()`, and in `KavaRef`,
you use `get<T>()` 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<MyClass>
.of(null) // Can be omitted and return to the call chain object FieldResolver<MyClass>
.get<String>() // 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<T>` and `Class<T>` 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. <u>**It no longer actively intercepts exceptions and prints error logs or even provides `onNoSuchMethod` listener**</u>.
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!
:::

View File

@@ -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 <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
return super.getDeclaredConstructors(declaringClass)
}
val constructors = HiddenApiBypass.getDeclaredMethods(declaringClass)
.filterIsInstance<Constructor<T>>()
.toList()
return constructors
}
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
return super.getDeclaredMethods(declaringClass)
}
val methods = HiddenApiBypass.getDeclaredMethods(declaringClass)
.filterIsInstance<Method>()
.toList()
return methods
}
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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")
```

View File

@@ -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)
<span style="margin-left: 5px"/>
![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 = "<version>"
[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 `<version>` 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:<version>")
```
Please change `<version>` 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<boolean, String> task, boolean isFinish) {
// ...
}
private void stop() {
// ...
}
private String getName() {
// ...
}
}
```
```java
public class Box<T> {
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<MethodResolver>
// 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<MethodResolver>` 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<Boolean>()
```
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<T>()` to specify its superclass type `BaseTest` for the actual object `Test`.
> The following example
```kotlin
val test = Test::class.resolve()
.firstConstructor {
emptyParameters()
}.createAsType<BaseTest>() // 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<Any>`, just like the following.
> The following example
```kotlin
val myClass = Class.forName("com.xxx.MyClass") as Class<Any>
// 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<String>`.
> The following example
```kotlin
// Suppose this is an example of this Class.
val box: Box<String>
// 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<String>() // 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<MethodResolver>.
```
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<Test>()
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<Test>()
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<Test>()
condition1.name = "doTask"
// Create a second condition.
val condition2 = MethodCondition<Test>()
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 <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
// Intercept and implement your constructor filtering logic here.
return super.getDeclaredConstructors(declaringClass)
}
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
// Intercept and implement your method filtering logic here.
return super.getDeclaredMethods(declaringClass)
}
override fun <T : Any> getDeclaredFields(declaringClass: Class<T>): List<Field> {
// 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");
}
}
```

View File

@@ -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)
<span style="margin-left: 5px"/>
![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 = "<version>"
[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 `<version>` 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:<version>")
```
Please change `<version>` 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<MyClass>()
```
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<MySuperClass>("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<MySuperClass>("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`.

17
docs-source/src/index.md Normal file
View File

@@ -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
---

View File

@@ -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

View File

@@ -0,0 +1,21 @@
# 更新日志
> 这里记录了 `KavaRef` 的版本更新历史。
::: danger
我们只会对最新的 API 版本进行维护,若你正在使用过时的 API 版本则代表你自愿放弃一切维护的可能性。
:::
## kavaref-core
### 1.0.0 | 2025.06.25 &ensp;<Badge type="tip" text="最新" vertical="middle" />
- 首个版本提交至 Maven
## kavaref-extension
### 1.0.0 | 2025.06.25 &ensp;<Badge type="tip" text="最新" vertical="middle" />
- 首个版本提交至 Maven

View File

@@ -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。

View File

@@ -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<String>()
// 调用 myMethod1
val methodValue = firstMethod { name = "myMethod1" }.invoke<String>("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
以上功能可能会在实际推出后有所变化,最终以实际版本的功能为准。
:::

View File

@@ -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` 的使用示例对比。
> 示例如下
<div style="display: flex; gap: 16px;">
<div style="flex: 1;">
<h4>KavaRef</h4>
```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!")
```
</div>
<div style="flex: 1;">
<h4>YukiReflection</h4>
```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!")
```
</div>
</div>
`KavaRef` 在任何时候开始反射都需要使用 `resolve()` 来创建反射作用域,不再对 `Class` 等实例直接进行扩展相关 `method`、`constructor` 方法以避免污染其作用域。
`KavaRef` 抛弃了 "Finder" 的设计理念,使用 "Filter" (过滤器) 的设计理念来获取反射结果,“查找” 不再是查找,而是 “过滤”。
`KavaRef` 取消了 `YukiReflection` 在结果实例中定义获取的 `Member` 为多重还是单一的设计方案,直接返回整个 `List<MemberResolver>`
你在上方看到的示例使用了 `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<String>()`、`get<Int>()`、`invoke<String>(...)`、`invoke<Int>(...)` 等方式来获取或调用对应类型的结果。
::: 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<MyClass>
.get<String>() // 值
```
正如上面所说,`get(...)` 在 `YukiReflection` 中是获取 `FieldFinder.Result.Instance` 对象,而不是值,要获取值并处理为指定类型,你需要调用 `string()` 或者 `cast<String>()`,而在 `KavaRef` 中是在 `MemberResolver` 中直接使用 `get<T>()` 来获取指定类型的值,`KavaRef` 对应 `YukiReflection` 的 `get(...)` 的用法是 `of(...)`。
所以上述示例在 `KavaRef` 中的完整写法应该为。
> 示例如下
```kotlin
// 由于调用的是静态实例,"of(null)" 可被省略
MyClass::class.resolve()
.firstField { name = "content" } // 已是调用链对象 FieldResolver<MyClass>
.of(null) // 可省略,返回调用链对象 FieldResolver<MyClass>
.get<String>() // 值
```
:::
`KavaRef` 不再对 `Method` 提供 `call` 方法,现在统一合并为 `invoke` (带泛型参数),同时 `KavaRef` 将 `Constructor` 的 `newInstance` 方法定义为 `create` (带泛型参数)。
你可能注意到条件 `superClass()` 消失了,它还在,在 `KavaRef` 中它已更名为 `superclass()`,对接标准的 Java 反射 API。
同时,`KavaRef` 对 `KClass` 进行了扩展,你不再需要在大部分场景中使用 `Some::class.java` 的方式来声明一个 `Class` 实例。
`KavaRef` 的另一个设计思想就是类型安全,只要是你在使用声明指定泛型类型的 `KClass<T>`、`Class<T>` 时,在 `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` 的异常逻辑将保持默认透明,<u>**它不再主动拦截异常并打印错误日志甚至是提供 `onNoSuchMethod` 监听**</u>,当没有过滤到任何有效的成员时,`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` 吧!
:::

View File

@@ -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 <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
return super.getDeclaredConstructors(declaringClass)
}
val constructors = HiddenApiBypass.getDeclaredMethods(declaringClass)
.filterIsInstance<Constructor<T>>()
.toList()
return constructors
}
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
return super.getDeclaredMethods(declaringClass)
}
val methods = HiddenApiBypass.getDeclaredMethods(declaringClass)
.filterIsInstance<Method>()
.toList()
return methods
}
}
```

View File

@@ -0,0 +1,5 @@
# R8 与 Proguard 混淆
> 大部分场景下 Android 应用程序安装包可通过混淆压缩体积,这里介绍了混淆规则的配置方法。
`KavaRef` 在 Android 项目中不需要额外配置任何混淆规则。

View File

@@ -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)。
## 语言要求
推荐使用 KotlinAPI 代码构成同样支持 Java但是在纯 Java 项目中 `KavaRef` 有可能无法发挥其全部功能和语法糖优势。
文档全部的 Demo 示例代码都将首先使用 Kotlin 进行描述,如果你完全不会使用 Kotlin 那你将有可能无法更全面地体验和使用 `KavaRef` 的功能。
## 功能贡献
本项目的维护离不开各位开发者的支持和贡献,目前这个项目处于初期阶段,可能依然存在一些问题或者缺少你需要的功能,
如果可能,欢迎提交 PR 为此项目贡献你认为需要的功能或前往 [GitHub Issues](repo://issues) 向我们提出建议。

View File

@@ -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) 找到一些示例,查看对应的演示项目来更好地了解这些功能的运作方式,快速地挑选出你需要的功能。

View File

@@ -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")
```

View File

@@ -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)
<span style="margin-left: 5px"/>
![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 = "<version>"
[libraries]
kavaref-core = { module = "com.highcapable.kavaref:kavaref-core", version.ref = "kavaref-core" }
```
在你的项目 `build.gradle.kts` 中配置依赖。
```kotlin
implementation(libs.kavaref.core)
```
请将 `<version>` 修改为此文档顶部显示的版本。
### 传统方式
在你的项目 `build.gradle.kts` 中配置依赖。
```kotlin
implementation("com.highcapable.kavaref:kavaref-core:<version>")
```
请将 `<version>` 修改为此文档顶部显示的版本。
## 功能介绍
你可以 [点击这里](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<boolean, String> task, boolean isFinish) {
// ...
}
private void stop() {
// ...
}
private String getName() {
// ...
}
}
```
```java
public class Box<T> {
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<MethodResolver> 实例
// 这里我们获取到过滤结果的第一个
.first()
// 在 MethodResolver 上设置 Test 的实例
.of(test)
// 调用方法并传入参数
.invoke("task_name")
```
在以上写法中,我们通过 `Test::class.resolve()` 来获取当前 `Class` 的 KavaRef 反射实例,
然后通过 `method { ... }` 来创建一个方法过滤条件 `MethodCondition`,在其中设置方法名和参数类型,执行后返回 `List<MethodResolver>` 实例,
接着我们通过 `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<Boolean>()
```
`Test` 中的构造方法是私有化的,现在,我们可以使用以下方式来创建它的实例。
> 示例如下
```kotlin
val test = Test::class.resolve()
.firstConstructor {
// 对于零参构造方法,可以使用以下条件过滤
// 它等价于 parameterCount = 0
emptyParameters()
}.create() // 创建一个新的 Test 实例
```
你也可以使用 `createAsType<T>()` 为实际对象 `Test` 指定其超类类型 `BaseTest`。
> 示例如下
```kotlin
val test = Test::class.resolve()
.firstConstructor {
emptyParameters()
}.createAsType<BaseTest>() // 创建一个新的 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<Any>`,就像下面这样。
> 示例如下
```kotlin
val myClass = Class.forName("com.xxx.MyClass") as Class<Any>
// 假设这就是这个 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<String>` 中的 `print` 方法。
> 示例如下
```kotlin
// 假设这就是这个 Class 的实例
val box: Box<String>
// 使用 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<String>() // 获取字段内容
```
你还可以在 `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<MethodResolver>
```
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<Test>()
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<Test>()
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<Test>()
condition1.name = "doTask"
// 创建第二个条件
val condition2 = MethodCondition<Test>()
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 <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
// 在这里拦截并实现你的构造方法过滤逻辑
return super.getDeclaredConstructors(declaringClass)
}
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
// 在这里拦截并实现你的方法过滤逻辑
return super.getDeclaredMethods(declaringClass)
}
override fun <T : Any> getDeclaredFields(declaringClass: Class<T>): List<Field> {
// 在这里拦截并实现你的字段过滤逻辑
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");
}
}
```

View File

@@ -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)
<span style="margin-left: 5px"/>
![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 = "<version>"
[libraries]
kavaref-extension = { module = "com.highcapable.kavaref:kavaref-extension", version.ref = "kavaref-extension" }
```
在你的项目 `build.gradle.kts` 中配置依赖。
```kotlin
implementation(libs.kavaref.extension)
```
请将 `<version>` 修改为此文档顶部显示的版本。
### 传统方式
在你的项目 `build.gradle.kts` 中配置依赖。
```kotlin
implementation("com.highcapable.kavaref:kavaref-extension:<version>")
```
请将 `<version>` 修改为此文档顶部显示的版本。
## 功能介绍
你可以 [点击这里](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<MyClass>()
```
你可以使用 `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<MySuperClass>("Hello", 123)
// 同样地,你也可以使用带有 OrNull 后缀的方法在创建失败时返回 null 而不是抛出异常
val mySuperClassOrNull = MyClass::class.createInstanceAsTypeOrNull<MySuperClass>("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`

2004
docs-source/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

25
gradle.properties Normal file
View File

@@ -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

View File

@@ -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: <this>::slf4j-api

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Sat May 10 20:11:35 CST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
gradlew vendored Executable file
View File

@@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# 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.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
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" "$@"

89
gradlew.bat vendored Normal file
View File

@@ -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

11
img-src/icon.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,10 @@
plugins {
id("java")
}
group = property.project.groupName
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

View File

@@ -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.
*
* <p>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 <a href="{@docRoot}studio/debug/am-logcat.html">view the logs in logcat</a>.
*
* <p>The order in terms of verbosity, from least to most is
* ERROR, WARN, INFO, DEBUG, VERBOSE.
*
* <p><b>Tip:</b> A good convention is to declare a <code>TAG</code> constant
* in your class:
*
* <pre>private static final String TAG = "MyActivity";</pre>
* <p>
* and use that in subsequent calls to the log methods.
* </p>
*
* <p><b>Tip:</b> Don't forget that when you make a call like
* <pre>Log.v(TAG, "index=" + i);</pre>
* 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.
*
* <p>When calling the log methods that take a Throwable parameter,
* if any of the throwables in the cause chain is an <code>UnknownHostException</code>,
* then the stack trace is not logged.
*
* <p>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!");
}
}

View File

@@ -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)
}

View File

@@ -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 <T : Any> KClass<T>.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 <T : Any> KClass<T>.resolve(block: MemberScope<T>.() -> 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 <T : Any> Class<T>.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 : Any> T.resolve() = when (this) {
is KClass<*> -> MemberScope((this as KClass<T>).java.createConfiguration(memberInstance = this))
is Class<*> -> MemberScope((this as Class<T>).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<MemberResolver<*, *>>.resolve(): List<MemberResolver<*, *>> = 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<T : Any> internal constructor(private val configuration: MemberCondition.Configuration<T>) {
/**
* 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<T>().also {
it.configuration = configuration
}
/**
* Start a new method reflection.
* @see firstMethod
* @see firstMethodOrNull
* @param condition the condition.
* @return [MethodResolver]
*/
fun method(condition: MethodCondition<T>) = 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<T>) = 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<T>) = 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<T>) = 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<T>) = method(condition).lastOrNull()
/**
* Start a new method reflection.
* @see firstMethod
* @see firstMethodOrNull
* @param condition the condition body.
* @return [MethodResolver]
*/
inline fun method(condition: MethodCondition<T>.() -> 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<T>.() -> 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<T>.() -> 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<T>.() -> 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<T>.() -> Unit = {}) = method(condition).lastOrNull()
/**
* Start a new constructor reflection.
* @return [ConstructorCondition]
*/
fun constructor() = ConstructorCondition<T>().also {
it.configuration = configuration
}
/**
* Start a new constructor reflection.
* @see firstConstructor
* @see firstConstructorOrNull
* @param condition the condition.
* @return [ConstructorResolver]
*/
fun constructor(condition: ConstructorCondition<T>) = 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<T>) = 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<T>) = 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<T>) = 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<T>) = constructor(condition).lastOrNull()
/**
* Start a new constructor reflection.
* @see firstConstructor
* @see firstConstructorOrNull
* @param condition the condition body.
* @return [ConstructorResolver]
*/
inline fun constructor(condition: ConstructorCondition<T>.() -> 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<T>.() -> 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<T>.() -> 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<T>.() -> 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<T>.() -> Unit = {}) = constructor(condition).lastOrNull()
/**
* Start a new field reflection.
* @return [FieldCondition]
*/
fun field() = FieldCondition<T>().also {
it.configuration = configuration
}
/**
* Start a new field reflection.
* @see firstField
* @see firstFieldOrNull
* @param condition the condition.
* @return [FieldResolver]
*/
fun field(condition: FieldCondition<T>) = 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<T>) = 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<T>) = 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<T>) = 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<T>) = field(condition).lastOrNull()
/**
* Start a new field reflection.
* @see firstField
* @see firstFieldOrNull
* @param condition the condition body.
* @return [FieldResolver]
*/
inline fun field(condition: FieldCondition<T>.() -> 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<T>.() -> 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<T>.() -> 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<T>.() -> 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<T>.() -> Unit = {}) = field(condition).lastOrNull()
}
}

View File

@@ -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<T : Any> : ExecutableCondition<Constructor<T>, ConstructorResolver<T>, 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<Modifiers>) -> 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<Class<*>>) -> 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<Any>) = apply { super.parameterAnnotations(*annotations) }
override fun parameterAnnotationsNot(vararg annotations: Set<Any>) = 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<T>().also {
initializeCopiedData(it)
}
override fun build(configuration: Configuration<T>?): List<ConstructorResolver<T>> {
configuration?.let { checkAndSetConfiguration(it) }
return MemberProcessor.resolve(condition = this, this.configuration)
}
override val conditionStringMap get() = super.conditionStringMap
}

View File

@@ -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<T : Any> : MemberCondition<Field, FieldResolver<T>, 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<Modifiers>) -> 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<Field, FieldResolver<T>, 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<Field, FieldResolver<T>, 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<T>().also {
initializeCopiedData(it)
}
override fun build(configuration: Configuration<T>?): List<FieldResolver<T>> {
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"
}
}

View File

@@ -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<T : Any> : ExecutableCondition<Method, MethodResolver<T>, 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<Modifiers>) -> 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<Class<*>>) -> 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<Any>) = apply { super.parameterAnnotations(*annotations) }
override fun parameterAnnotationsNot(vararg annotations: Set<Any>) = 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<Method, MethodResolver<T>, 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<Method, MethodResolver<T>, 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<T>().also {
initializeCopiedData(it)
}
override fun build(configuration: Configuration<T>?): List<MethodResolver<T>> {
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"
}
}

View File

@@ -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<E : Executable, R : MemberResolver<E, T>, T : Any> : MemberCondition<E, R, T>() {
/** @see Executable.getParameterTypes */
val parameters = mutableListOf<Any>()
/** @see Executable.getParameterTypes */
val parametersNot = mutableListOf<Any>()
/** @see Executable.getParameterTypes */
var parametersCondition: ((List<Class<*>>) -> Boolean)? = null
/** @see Executable.getTypeParameters */
val typeParameters = mutableSetOf<TypeMatcher>()
/** @see Executable.getTypeParameters */
val typeParametersNot = mutableSetOf<TypeMatcher>()
/** @see Executable.getParameterCount */
var parameterCount: Int? = null
/** @see Executable.getParameterCount */
var parameterCountCondition: ((Int) -> Boolean)? = null
/** @see Executable.getExceptionTypes */
val exceptionTypes = mutableSetOf<Any>()
/** @see Executable.getExceptionTypes */
val exceptionTypesNot = mutableSetOf<Any>()
/** @see Executable.getGenericExceptionTypes */
val genericExceptionTypes = mutableSetOf<TypeMatcher>()
/** @see Executable.getGenericExceptionTypes */
val genericExceptionTypesNot = mutableSetOf<TypeMatcher>()
/** @see Executable.getGenericParameterTypes */
val genericParameters = mutableSetOf<TypeMatcher>()
/** @see Executable.getGenericParameterTypes */
val genericParametersNot = mutableSetOf<TypeMatcher>()
/** @see Executable.isVarArgs */
var isVarArgs: Boolean? = null
/** @see Executable.isVarArgs */
var isVarArgsNot: Boolean? = null
/** @see Executable.getParameterAnnotations */
val parameterAnnotations = mutableListOf<Set<Any>>()
/** @see Executable.getParameterAnnotations */
val parameterAnnotationsNot = mutableListOf<Set<Any>>()
/** @see Executable.getAnnotatedReturnType */
val annotatedReturnType = mutableSetOf<Any>()
/** @see Executable.getAnnotatedReturnType */
val annotatedReturnTypeNot = mutableSetOf<Any>()
/** @see Executable.getAnnotatedReceiverType */
val annotatedReceiverType = mutableSetOf<Any>()
/** @see Executable.getAnnotatedReceiverType */
val annotatedReceiverTypeNot = mutableSetOf<Any>()
/** @see Executable.getAnnotatedParameterTypes */
val annotatedParameterTypes = mutableSetOf<Any>()
/** @see Executable.getAnnotatedParameterTypes */
val annotatedParameterTypesNot = mutableSetOf<Any>()
/** @see Executable.getAnnotatedExceptionTypes */
val annotatedExceptionTypes = mutableSetOf<Any>()
/** @see Executable.getAnnotatedExceptionTypes */
val annotatedExceptionTypesNot = mutableSetOf<Any>()
/** @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<Class<*>>) -> 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<Any>) = apply {
this.parameterAnnotations.addAll(annotations)
}
/** @see Executable.getParameterAnnotations */
open fun parameterAnnotationsNot(vararg annotations: Set<Any>) = 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<E, R, T>) {
super.initializeCopiedData(newSelf)
(newSelf as? ExecutableCondition<E, R, T>)?.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<E, R, T>) {
super.initializeMergedData(other)
(other as? ExecutableCondition<E, R, T>)?.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"
}
}

View File

@@ -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<M : Member, R : MemberResolver<M, T>, 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<T : Any>(
val declaringClass: Class<T>,
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 <T : Any> Class<T>.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<T>? = null
/** @see Member.getName */
var name: String? = null
/** @see Member.getName */
var nameCondition: ((String) -> Boolean)? = null
/** @see Member.getModifiers */
val modifiers = mutableSetOf<Modifiers>()
/** @see Member.getModifiers */
val modifiersNot = mutableSetOf<Modifiers>()
/** @see Member.getModifiers */
var modifiersCondition: ((Set<Modifiers>) -> Boolean)? = null
/** @see Member.isSynthetic */
var isSynthetic: Boolean? = null
/** @see Member.isSynthetic */
var isSyntheticNot: Boolean? = null
/** @see AnnotatedElement.getDeclaredAnnotations */
val annotations = mutableSetOf<Any>()
/** @see AnnotatedElement.getDeclaredAnnotations */
val annotationsNot = mutableSetOf<Any>()
/**
* @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<Modifiers>) -> 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<M, R, T>) {
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<M, R, T>) {
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<M, R, T>
/**
* 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<T>? = null): List<R> =
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<T>) {
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
}
}

View File

@@ -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 <M : Member, R : MemberResolver<M, T>, T : Any, U : MemberCondition<M, R, T>> U.mergeWith(other: U) = initializeMergedData(other)

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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>
) : 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)
}
}

View File

@@ -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
}

View File

@@ -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<TypeMatcher> = emptyList(),
val lowerBounds: List<TypeMatcher> = 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)
}
}

View File

@@ -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
}

View File

@@ -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<Type>.matchesAll(matchers: List<TypeMatcher>): Boolean {
if (this.size != matchers.size) return false
return this.zip(matchers).all { (t, m) -> m.matches(t) }
}

View File

@@ -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()
}
}

View File

@@ -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<VagueType>())
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()
}
}

View File

@@ -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<T : Any> internal constructor(override val self: Constructor<T>) : MemberResolver<Constructor<T>, 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 <reified T : Any> createAsType(vararg args: Any?): T {
self.makeAccessible()
return self.newInstance(*args) as? T ?: error("$this's instance cannot be cast to type ${classOf<T>()}.")
}
/**
* 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 <reified T : Any> createAsTypeQuietly(vararg args: Any?) = runCatching { createAsType<T>(*args) }.getOrNull()
}

View File

@@ -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<T : Any> internal constructor(override val self: Field) : InstanceAwareResolver<Field, T>(self) {
override fun of(instance: T?) = apply {
checkAndSetInstance(instance)
}
override fun copy() = FieldResolver<T>(self)
/**
* Get the value of the field.
* @see Field.get
* @see getQuietly
* @return [T] or null.
*/
@JvmName("getTyped")
fun <T : Any?> 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 <T : Any?> getQuietly() = runCatching { get<T>() }.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
}

View File

@@ -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<T : Any> internal constructor(override val self: Method) : InstanceAwareResolver<Method, T>(self) {
override fun of(instance: T?) = apply {
checkAndSetInstance(instance)
}
override fun copy() = MethodResolver<T>(self)
/**
* Invoke the method with the given arguments.
* @see Method.invoke
* @see invokeQuietly
* @return [T] or null.
*/
@JvmName("invokeTyped")
fun <T : Any?> 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 <T : Any?> invokeQuietly(vararg args: Any?) = runCatching { invoke<T>(*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()
}

View File

@@ -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<M : Member, T : Any>(override val self: M) : MemberResolver<M, T>(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<M, T>
/**
* 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
}
}

View File

@@ -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<M : Member, T : Any>(open val self: M) {
/**
* Create a copy of this resolver.
* @return [MemberResolver]<[M]>
*/
abstract fun copy(): MemberResolver<M, T>
}

View File

@@ -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 <T : Any> getDeclaredFields(declaringClass: Class<T>): List<Field> =
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 <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> =
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 <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> =
runCatching { declaringClass.declaredConstructors.filterIsInstance<Constructor<T>>() }.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 <reified M : Member, reified R : MemberResolver<M, T>, T : Any> resolve(
condition: MemberCondition<M, R, T>,
configuration: MemberCondition.Configuration<T>?
): List<R> {
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<R>
}
private fun <M : Member, R : MemberResolver<M, T>, T : Any> resolveInClass(
condition: MemberCondition<M, R, T>,
configuration: MemberCondition.Configuration<T>,
declaringClass: Class<*>?,
result: (declaringClass: Class<*>) -> List<R>
): List<R> {
if (declaringClass == null || declaringClass == classOf<Any>())
return throwIfNotOptional(condition, configuration)
return result(declaringClass).ifEmpty {
if (configuration.superclass)
resolveInClass(condition, configuration, declaringClass.superclass, result)
else throwIfNotOptional(condition, configuration)
}
}
private fun <T : Any> methodFilters(
condition: MethodCondition<T>,
configuration: MemberCondition.Configuration<T>,
declaringClass: Class<*>
): List<MethodResolver<T>> = 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 <T : Any> constructorFilters(
condition: ConstructorCondition<T>,
configuration: MemberCondition.Configuration<T>,
declaringClass: Class<*>
): List<ConstructorResolver<T>> = configuration.currentProcessorResolver.getDeclaredConstructors(declaringClass)
.asSequence()
.baseFilters(condition, configuration)
.executableFilters(condition, configuration)
.resolve(configuration)
private fun <T : Any> fieldFilters(
condition: FieldCondition<T>,
configuration: MemberCondition.Configuration<T>,
declaringClass: Class<*>
): List<FieldResolver<T>> = 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 <M : Member, T : Any> Sequence<M>.baseFilters(
condition: MemberCondition<*, *, *>,
configuration: MemberCondition.Configuration<T>
) = 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 <M : Executable, T : Any> Sequence<M>.executableFilters(
condition: ExecutableCondition<*, *, *>,
configuration: MemberCondition.Configuration<T>
) = 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 <reified M : Member, reified R : MemberResolver<M, T>, T : Any> Sequence<M>.resolve(
configuration: MemberCondition.Configuration<T>
) = when (M::class) {
Method::class -> map { MethodResolver<T>(it as Method).apply(configuration) }
Constructor::class -> map { ConstructorResolver(it as Constructor<T>).apply(configuration) }
Field::class -> map { FieldResolver<T>(it as Field).apply(configuration) }
else -> error("Unsupported member type: $this")
}.toList() as List<R>
private fun <M : Member, R : MemberResolver<M, T>, T : Any> throwIfNotOptional(
condition: MemberCondition<M, R, T>,
configuration: MemberCondition.Configuration<T>
): List<R> {
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 <reified M : Member, T : Any> MemberResolver<M, T>.apply(configuration: MemberCondition.Configuration<T>) =
apply { configuration.memberInstance?.let { if (this is InstanceAwareResolver) of(it) } }
private fun <T, R> Sequence<T>.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 <T : Any> compareElementTypes(
conditionKey: Collection<Any>,
typesValue: List<Class<*>>,
configuration: MemberCondition.Configuration<T>
): 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<VagueType>() || target == type
}.size == typesValue.size
return isMatched
}
@JvmName("compareElementTypesMultiple")
private fun <T : Any> compareElementTypes(
conditionKey: Collection<Collection<Any>>,
typesValue: List<List<Class<*>>>,
configuration: MemberCondition.Configuration<T>
): 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<TypeMatcher>,
typesValue: List<Type>
): 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 <T : Any> buildConditionTable(
condition: MemberCondition<*, *, *>,
configuration: MemberCondition.Configuration<T>
) = 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 <T : Any> MemberCondition.Configuration<T>.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 <T : Any> Any.toTypeClass(configuration: MemberCondition.Configuration<T>, noVague: String? = null): Class<*> {
fun Class<*>.parseVagueType() =
if (this == classOf<VagueType>())
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<Any>()
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)
}

View File

@@ -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
}

View File

@@ -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"
)
}
}

View File

@@ -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<Array>
/**
* 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))

View File

@@ -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<String, Constructor<*>>()
/**
* 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 <T : Any> load(loader: ClassLoader? = null, initialize: Boolean = false) = load(loader, initialize) as? Class<T>?
?: 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 <T : Any> loadOrNull(loader: ClassLoader? = null, initialize: Boolean = false) = loadOrNull(loader, initialize) as? Class<T>?
}
/**
* 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<T : Any> private constructor(
private val classDefinition: Any,
private val initialize: Boolean,
private val loader: ClassLoaderInitializer?
) {
private var baseDefinition: Class<T>? = null
/**
* Get non-null instance of [Class].
* @return [Class]<[T]>
*/
@get:JvmSynthetic
internal val nonNull get(): Class<T> {
if (baseDefinition == null)
baseDefinition = when (classDefinition) {
is String -> classDefinition.toClass<T>(loader?.invoke(), initialize)
is VariousClass -> classDefinition.load<T>(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<T>? {
if (baseDefinition == null)
baseDefinition = when (classDefinition) {
is String -> classDefinition.toClassOrNull<T>(loader?.invoke(), initialize)
is VariousClass -> classDefinition.loadOrNull<T>(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<T : Any> internal constructor(
classDefinition: Any,
initialize: Boolean,
loader: ClassLoaderInitializer?
) : LazyClass<T>(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<T : Any> internal constructor(
classDefinition: Any,
initialize: Boolean,
loader: ClassLoaderInitializer?
) : LazyClass<T>(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<Any>(name, initialize, loader)
/**
* Create a non-null instance of [Class].
* @see lazyClass
* @see lazyClassOrNull
* @return [LazyClass.NonNull]<[T]>
*/
@JvmSynthetic
@JvmName("lazyClassTyped")
fun <T : Any> lazyClass(name: String, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) =
LazyClass.NonNull<T>(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<Any>(variousClass, initialize, loader)
/**
* Create a non-null instance of [VariousClass].
* @see lazyClass
* @see lazyClassOrNull
* @return [LazyClass.NonNull]<[T]>
*/
@JvmSynthetic
@JvmName("lazyClassTyped")
fun <T : Any> lazyClass(variousClass: VariousClass, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) =
LazyClass.NonNull<T>(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<Any>(name, initialize, loader)
/**
* Create a nullable instance of [Class].
* @see lazyClass
* @see lazyClassOrNull
* @return [LazyClass.Nullable]<[T]>
*/
@JvmSynthetic
@JvmName("lazyClassOrNullTyped")
fun <T : Any> lazyClassOrNull(name: String, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) =
LazyClass.Nullable<T>(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<Any>(variousClass, initialize, loader)
/**
* Create a nullable instance of [VariousClass].
* @see lazyClass
* @see lazyClassOrNull
* @return [LazyClass.Nullable]<[T]>
*/
@JvmSynthetic
@JvmName("lazyClassOrNullTyped")
fun <T : Any> lazyClassOrNull(variousClass: VariousClass, initialize: Boolean = false, loader: ClassLoaderInitializer? = null) =
LazyClass.Nullable<T>(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<Any> {
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<Any>
}
/**
* Convert [String] class name to [Class] with [ClassLoader] and initialize.
* @see Class.toClass
* @see String.toClassOrNull
* @return [Class]<[T]>
*/
@JvmOverloads
@JvmName("createTyped")
fun <T : Any> String.toClass(loader: ClassLoader? = null, initialize: Boolean = false) =
toClass(loader, initialize) as? Class<T>? ?: 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 <T : Any> String.toClassOrNull(loader: ClassLoader? = null, initialize: Boolean = false) =
toClassOrNull(loader, initialize) as? Class<T>?
/**
* 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 <T : Any> Class<T>.createInstance(vararg args: Any?, isPublic: Boolean = true): T {
fun Class<*>.wrap() = when (this) {
JBoolean.TYPE -> classOf<JBoolean>(primitiveType = false)
JByte.TYPE -> classOf<JByte>(primitiveType = false)
JCharacter.TYPE -> classOf<JCharacter>(primitiveType = false)
JShort.TYPE -> classOf<JShort>(primitiveType = false)
JInteger.TYPE -> classOf<JInteger>(primitiveType = false)
JLong.TYPE -> classOf<JLong>(primitiveType = false)
JFloat.TYPE -> classOf<JFloat>(primitiveType = false)
JDouble.TYPE -> classOf<JDouble>(primitiveType = false)
JVoid.TYPE -> classOf<JVoid>(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 <T : Any> KClass<T>.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 <T : Any> Class<T>.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 <T : Any> KClass<T>.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 <reified T : Any> Class<*>.createInstanceAsType(vararg args: Any?, isPublic: Boolean = true) =
createInstance(*args, isPublic = isPublic) as? T ?: error("$this's instance cannot be cast to type ${classOf<T>()}.")
/**
* Create an instance of [KClass.java] with the given arguments and cast it to the specified type [T].
* @see Class.createInstanceAsType
*/
inline fun <reified T : Any> KClass<*>.createInstanceAsType(vararg args: Any?, isPublic: Boolean = true) =
java.createInstanceAsType<T>(*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 <reified T : Any> Class<*>.createInstanceAsTypeOrNull(vararg args: Any?, isPublic: Boolean = true) =
runCatching { createInstanceAsType<T>(*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 <reified T : Any> KClass<*>.createInstanceAsTypeOrNull(vararg args: Any?, isPublic: Boolean = true) =
java.createInstanceAsTypeOrNull<T>(*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<Any>? }.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 <reified T : Any> 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 <T : Any> Class<T>.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 <T : Any> KClass<T>.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 <T : Any> KClass<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.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 <T : Any> KClass<T>.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 <T : Any> KClass<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.hasSuperclass get() = superclass != null && superclass != classOf<Any>()
/**
* 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 <T : Any> KClass<T>.hasSuperclass get() = java.hasSuperclass
/**
* Whether the current [Class] has implemented interfaces.
* @receiver the [Class] to be checked.
* @return [Boolean]
*/
val <T : Any> Class<T>.hasInterfaces get() = interfaces.isNotEmpty()
/**
* Whether the current [KClass.java] has implemented interfaces.
* @see Class.hasInterfaces
*/
@get:JvmSynthetic
val <T : Any> KClass<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.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 <T : Any> Class<T>.isStrict get() = Modifier.isStrict(modifiers)

View File

@@ -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 <reified T : Member> 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)

View File

@@ -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 <T : Any> Type.toClass(): Class<T> = when (this) {
is Class<*> -> this as Class<T>
is ParameterizedType -> rawType.toClass<T>()
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<Any>()
/**
* 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 <T : Any> Type.toClassOrNull() = runCatching { toClass<T>() }.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<Any>()
/**
* Convert [Type] to [ParameterizedType].
* @see Type.asParameterizedTypeOrNull
* @receiver the [Class] to get the [ParameterizedType].
* @return [ParameterizedType]
*/
inline fun <reified T : Type> 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 <reified T : Type> 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 <T : Any> Class<T>.genericSuperclassTypeArguments(): Array<Type> = 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 <T : Any> KClass<T>.genericSuperclassTypeArguments() = java.genericSuperclassTypeArguments()

View File

@@ -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

View File

@@ -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)
}

Some files were not shown because too many files have changed in this diff Show More