mirror of
https://github.com/HighCapable/KavaRef.git
synced 2025-09-03 17:25:27 +08:00
Initial commit
This commit is contained in:
35
.editorconfig
Normal file
35
.editorconfig
Normal 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
110
.gitignore
vendored
Normal 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
8
.idea/.gitignore
generated
vendored
Normal 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
|
1
.idea/fileTemplates/includes/copyright-name.template
generated
Normal file
1
.idea/fileTemplates/includes/copyright-name.template
generated
Normal file
@@ -0,0 +1 @@
|
||||
2019 HighCapable
|
13
.idea/fileTemplates/includes/license-content.template
generated
Normal file
13
.idea/fileTemplates/includes/license-content.template
generated
Normal 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.
|
13
.idea/fileTemplates/includes/open-source-license-header.template
generated
Normal file
13
.idea/fileTemplates/includes/open-source-license-header.template
generated
Normal 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.
|
||||
*/
|
1
.idea/fileTemplates/includes/project-description.template
generated
Normal file
1
.idea/fileTemplates/includes/project-description.template
generated
Normal file
@@ -0,0 +1 @@
|
||||
A modernizing Java Reflection with Kotlin.
|
1
.idea/fileTemplates/includes/project-name.template
generated
Normal file
1
.idea/fileTemplates/includes/project-name.template
generated
Normal file
@@ -0,0 +1 @@
|
||||
KavaRef
|
1
.idea/fileTemplates/includes/project-url.template
generated
Normal file
1
.idea/fileTemplates/includes/project-url.template
generated
Normal file
@@ -0,0 +1 @@
|
||||
https://github.com/HighCapable/KavaRef
|
6
.idea/fileTemplates/internal/Kotlin Annotation.kt
generated
Normal file
6
.idea/fileTemplates/internal/Kotlin Annotation.kt
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
#end
|
||||
|
||||
annotation class ${NAME}
|
7
.idea/fileTemplates/internal/Kotlin Class.kt
generated
Normal file
7
.idea/fileTemplates/internal/Kotlin Class.kt
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
|
||||
#end
|
||||
class ${NAME} {
|
||||
}
|
6
.idea/fileTemplates/internal/Kotlin Data Class.kt
generated
Normal file
6
.idea/fileTemplates/internal/Kotlin Data Class.kt
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
#end
|
||||
|
||||
data class ${NAME}()
|
7
.idea/fileTemplates/internal/Kotlin Enum.kt
generated
Normal file
7
.idea/fileTemplates/internal/Kotlin Enum.kt
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
|
||||
#end
|
||||
enum class ${NAME} {
|
||||
}
|
5
.idea/fileTemplates/internal/Kotlin File.kt
generated
Normal file
5
.idea/fileTemplates/internal/Kotlin File.kt
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
|
||||
#end
|
7
.idea/fileTemplates/internal/Kotlin Interface.kt
generated
Normal file
7
.idea/fileTemplates/internal/Kotlin Interface.kt
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
|
||||
#end
|
||||
interface ${NAME} {
|
||||
}
|
7
.idea/fileTemplates/internal/Kotlin Object.kt
generated
Normal file
7
.idea/fileTemplates/internal/Kotlin Object.kt
generated
Normal 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
11
.idea/icon.svg
generated
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal 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
6
.idea/kotlinc.xml
generated
Normal 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
7
.idea/ktlint-plugin.xml
generated
Normal 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
6
.idea/vcs.xml
generated
Normal 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
202
LICENSE
Normal 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
72
README-zh-CN.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# KavaRef
|
||||
|
||||
[](https://github.com/HighCapable/KavaRef/blob/master/LICENSE)
|
||||
[](https://github.com/HighCapable/KavaRef/releases)
|
||||
[](https://t.me/KavaRef)
|
||||
[](https://t.me/HighCapable_Dev)
|
||||
[](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
|
||||
|
||||

|
||||
|
||||
## 许可证
|
||||
|
||||
- [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
75
README.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# KavaRef
|
||||
|
||||
[](https://github.com/HighCapable/KavaRef/blob/master/LICENSE)
|
||||
[](https://github.com/HighCapable/KavaRef/releases)
|
||||
[](https://t.me/KavaRef)
|
||||
[](https://t.me/HighCapable_Dev)
|
||||
[](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
|
||||
|
||||

|
||||
|
||||
## 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
57
build.gradle.kts
Normal 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
4
docs-source/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/node_modules
|
||||
/src/.vuepress/.cache
|
||||
/src/.vuepress/.temp
|
||||
/dist
|
3
docs-source/.vscode/settings.json
vendored
Normal file
3
docs-source/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
2
docs-source/build-dokka.sh
Executable file
2
docs-source/build-dokka.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
cd ..
|
||||
./gradlew :kavaref-core:publishKDoc :kavaref-extension:publishKDoc
|
17
docs-source/package.json
Normal file
17
docs-source/package.json
Normal 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": {}
|
||||
}
|
64
docs-source/src/.vuepress/config.ts
Normal file
64
docs-source/src/.vuepress/config.ts
Normal 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: '搜索' }
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
148
docs-source/src/.vuepress/configs/template.ts
Normal file
148
docs-source/src/.vuepress/configs/template.ts
Normal 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')
|
||||
}]
|
||||
};
|
39
docs-source/src/.vuepress/configs/utils.ts
Normal file
39
docs-source/src/.vuepress/configs/utils.ts
Normal 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);
|
||||
};
|
||||
}
|
||||
};
|
11
docs-source/src/.vuepress/public/images/logo.svg
Normal file
11
docs-source/src/.vuepress/public/images/logo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
179
docs-source/src/.vuepress/styles/index.scss
Normal file
179
docs-source/src/.vuepress/styles/index.scss
Normal 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};
|
||||
}
|
||||
}
|
27
docs-source/src/en/about/about.md
Normal file
27
docs-source/src/en/about/about.md
Normal 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
|
29
docs-source/src/en/about/changelog.md
Normal file
29
docs-source/src/en/about/changelog.md
Normal 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  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
||||
|
||||
## kavaref-extension
|
||||
|
||||
### 1.0.0 | 2025.06.25  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
16
docs-source/src/en/about/contacts.md
Normal file
16
docs-source/src/en/about/contacts.md
Normal 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.
|
114
docs-source/src/en/about/future.md
Normal file
114
docs-source/src/en/about/future.md
Normal 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.
|
||||
|
||||
:::
|
266
docs-source/src/en/config/migration.md
Normal file
266
docs-source/src/en/config/migration.md
Normal 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!
|
||||
|
||||
:::
|
37
docs-source/src/en/config/processor-resolvers.md
Normal file
37
docs-source/src/en/config/processor-resolvers.md
Normal 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
|
||||
}
|
||||
}
|
6
docs-source/src/en/config/r8-proguard.md
Normal file
6
docs-source/src/en/config/r8-proguard.md
Normal 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.
|
46
docs-source/src/en/guide/home.md
Normal file
46
docs-source/src/en/guide/home.md
Normal 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.
|
97
docs-source/src/en/guide/quick-start.md
Normal file
97
docs-source/src/en/guide/quick-start.md
Normal 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.
|
38
docs-source/src/en/index.md
Normal file
38
docs-source/src/en/index.md
Normal 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")
|
||||
```
|
790
docs-source/src/en/library/kavaref-core.md
Normal file
790
docs-source/src/en/library/kavaref-core.md
Normal file
@@ -0,0 +1,790 @@
|
||||
# kavaref-core
|
||||
|
||||

|
||||
<span style="margin-left: 5px"/>
|
||||

|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
```
|
315
docs-source/src/en/library/kavaref-extension.md
Normal file
315
docs-source/src/en/library/kavaref-extension.md
Normal file
@@ -0,0 +1,315 @@
|
||||
# kavaref-extension
|
||||
|
||||

|
||||
<span style="margin-left: 5px"/>
|
||||

|
||||
|
||||
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
17
docs-source/src/index.md
Normal 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
|
||||
---
|
27
docs-source/src/zh-cn/about/about.md
Normal file
27
docs-source/src/zh-cn/about/about.md
Normal 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
|
21
docs-source/src/zh-cn/about/changelog.md
Normal file
21
docs-source/src/zh-cn/about/changelog.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 更新日志
|
||||
|
||||
> 这里记录了 `KavaRef` 的版本更新历史。
|
||||
|
||||
::: danger
|
||||
|
||||
我们只会对最新的 API 版本进行维护,若你正在使用过时的 API 版本则代表你自愿放弃一切维护的可能性。
|
||||
|
||||
:::
|
||||
|
||||
## kavaref-core
|
||||
|
||||
### 1.0.0 | 2025.06.25  <Badge type="tip" text="最新" vertical="middle" />
|
||||
|
||||
- 首个版本提交至 Maven
|
||||
|
||||
## kavaref-extension
|
||||
|
||||
### 1.0.0 | 2025.06.25  <Badge type="tip" text="最新" vertical="middle" />
|
||||
|
||||
- 首个版本提交至 Maven
|
15
docs-source/src/zh-cn/about/contacts.md
Normal file
15
docs-source/src/zh-cn/about/contacts.md
Normal 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。
|
113
docs-source/src/zh-cn/about/future.md
Normal file
113
docs-source/src/zh-cn/about/future.md
Normal 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
|
||||
|
||||
以上功能可能会在实际推出后有所变化,最终以实际版本的功能为准。
|
||||
|
||||
:::
|
243
docs-source/src/zh-cn/config/migration.md
Normal file
243
docs-source/src/zh-cn/config/migration.md
Normal 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` 吧!
|
||||
|
||||
:::
|
38
docs-source/src/zh-cn/config/processor-resolvers.md
Normal file
38
docs-source/src/zh-cn/config/processor-resolvers.md
Normal 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
|
||||
}
|
||||
}
|
||||
```
|
5
docs-source/src/zh-cn/config/r8-proguard.md
Normal file
5
docs-source/src/zh-cn/config/r8-proguard.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# R8 与 Proguard 混淆
|
||||
|
||||
> 大部分场景下 Android 应用程序安装包可通过混淆压缩体积,这里介绍了混淆规则的配置方法。
|
||||
|
||||
`KavaRef` 在 Android 项目中不需要额外配置任何混淆规则。
|
36
docs-source/src/zh-cn/guide/home.md
Normal file
36
docs-source/src/zh-cn/guide/home.md
Normal 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)。
|
||||
|
||||
## 语言要求
|
||||
|
||||
推荐使用 Kotlin,API 代码构成同样支持 Java,但是在纯 Java 项目中 `KavaRef` 有可能无法发挥其全部功能和语法糖优势。
|
||||
|
||||
文档全部的 Demo 示例代码都将首先使用 Kotlin 进行描述,如果你完全不会使用 Kotlin 那你将有可能无法更全面地体验和使用 `KavaRef` 的功能。
|
||||
|
||||
## 功能贡献
|
||||
|
||||
本项目的维护离不开各位开发者的支持和贡献,目前这个项目处于初期阶段,可能依然存在一些问题或者缺少你需要的功能,
|
||||
如果可能,欢迎提交 PR 为此项目贡献你认为需要的功能或前往 [GitHub Issues](repo://issues) 向我们提出建议。
|
95
docs-source/src/zh-cn/guide/quick-start.md
Normal file
95
docs-source/src/zh-cn/guide/quick-start.md
Normal 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) 找到一些示例,查看对应的演示项目来更好地了解这些功能的运作方式,快速地挑选出你需要的功能。
|
38
docs-source/src/zh-cn/index.md
Normal file
38
docs-source/src/zh-cn/index.md
Normal 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")
|
||||
```
|
768
docs-source/src/zh-cn/library/kavaref-core.md
Normal file
768
docs-source/src/zh-cn/library/kavaref-core.md
Normal file
@@ -0,0 +1,768 @@
|
||||
# kavaref-core
|
||||
|
||||

|
||||
<span style="margin-left: 5px"/>
|
||||

|
||||
|
||||
这是 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");
|
||||
}
|
||||
}
|
||||
```
|
295
docs-source/src/zh-cn/library/kavaref-extension.md
Normal file
295
docs-source/src/zh-cn/library/kavaref-extension.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# kavaref-extension
|
||||
|
||||

|
||||
<span style="margin-left: 5px"/>
|
||||

|
||||
|
||||
这是 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
2004
docs-source/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
25
gradle.properties
Normal file
25
gradle.properties
Normal 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
|
28
gradle/sweet-dependency/sweet-dependency-config.yaml
Normal file
28
gradle/sweet-dependency/sweet-dependency-config.yaml
Normal 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
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal 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
234
gradlew
vendored
Executable 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
89
gradlew.bat
vendored
Normal 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
11
img-src/icon.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
10
kavaref-android-stub/build.gradle.kts
Normal file
10
kavaref-android-stub/build.gradle.kts
Normal file
@@ -0,0 +1,10 @@
|
||||
plugins {
|
||||
id("java")
|
||||
}
|
||||
|
||||
group = property.project.groupName
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
119
kavaref-android-stub/src/main/java/android/util/Log.java
Normal file
119
kavaref-android-stub/src/main/java/android/util/Log.java
Normal 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!");
|
||||
}
|
||||
}
|
34
kavaref-core/build.gradle.kts
Normal file
34
kavaref-core/build.gradle.kts
Normal 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)
|
||||
}
|
433
kavaref-core/src/main/kotlin/com/highcapable/kavaref/KavaRef.kt
Normal file
433
kavaref-core/src/main/kotlin/com/highcapable/kavaref/KavaRef.kt
Normal 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()
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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"
|
||||
}
|
||||
}
|
@@ -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"
|
||||
}
|
||||
}
|
@@ -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"
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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)
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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) }
|
||||
}
|
@@ -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()
|
||||
}
|
||||
}
|
@@ -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()
|
||||
}
|
||||
}
|
@@ -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()
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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()
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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>
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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
|
||||
}
|
26
kavaref-extension/build.gradle.kts
Normal file
26
kavaref-extension/build.gradle.kts
Normal 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"
|
||||
)
|
||||
}
|
||||
}
|
@@ -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))
|
@@ -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)
|
@@ -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)
|
@@ -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()
|
@@ -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
|
28
samples/kavaref-demo/build.gradle.kts
Normal file
28
samples/kavaref-demo/build.gradle.kts
Normal 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
Reference in New Issue
Block a user