17 Commits
1.0.0 ... 1.0.2

Author SHA1 Message Date
fb7682d963 Bump version to 1.0.2 2023-09-07 01:46:31 +08:00
9a91fdb0bf chore: bump "com.highcapable.sweetdependency" version to 1.0.1 2023-09-07 00:39:26 +08:00
4ab7eef69c docs: update guide 2023-09-07 00:16:33 +08:00
180f475562 feat: add keyValuesRules function 2023-09-07 00:15:45 +08:00
9e2c26ae73 feat: add propertiesFileNames function 2023-09-07 00:14:08 +08:00
f66cdc1f31 feat: add toStringMap function 2023-09-07 00:14:08 +08:00
2e73f36bf0 feat: add includeKeys function 2023-09-07 00:14:07 +08:00
8594bca9a6 refactor: using new way to checking load build script class exists 2023-09-06 19:33:10 +08:00
8cc09e7fcd refactor: using relative pom path to resolve generated dependencies jar 2023-09-06 19:32:08 +08:00
6297cb9919 refactor: replace packaging jar method using "net.lingala.zip4j" 2023-09-06 02:26:30 +08:00
c982fec8c5 Bump version to 1.0.1 2023-09-04 02:38:51 +08:00
eff8560bd2 docs: update guide 2023-09-04 02:01:40 +08:00
eead3c8839 feat: add use '' or "" to force a string value 2023-09-04 02:01:08 +08:00
e113c45a5d feat: let generator use typed value 2023-09-04 02:00:16 +08:00
6a0c581ccb docs: add readme badge link 2023-09-04 01:05:52 +08:00
62207002c4 docs: update readme badge 2023-09-04 00:56:46 +08:00
8747853310 docs: add license 2023-09-03 02:33:31 +08:00
23 changed files with 595 additions and 143 deletions

202
LICENSE Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright HighCapable [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,8 +1,8 @@
# Sweet Property
[![Blank](https://img.shields.io/badge/license-Apache2.0-blue)](https://github.com/HighCapable/SweetProperty/blob/master/LICENSE)
![Blank](https://img.shields.io/badge/version-v1.0.0-green)
[![Telegram](https://img.shields.io/badge/Discussion-Telegram-blue.svg?logo=telegram)](https://t.me/HighCapable_Dev)
[![GitHub license](https://img.shields.io/github/license/HighCapable/SweetProperty?color=blue)](https://github.com/HighCapable/SweetProperty/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/v/release/HighCapable/SweetProperty?display_name=release&logo=github&color=green)](https://github.com/HighCapable/SweetProperty/releases)
[![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram)](https://t.me/HighCapable_Dev)
<img src="https://github.com/HighCapable/SweetProperty/blob/master/img-src/icon.png?raw=true" width = "100" height = "100" alt="LOGO"/>

View File

@@ -1,8 +1,8 @@
# Sweet Property
[![Blank](https://img.shields.io/badge/license-Apache2.0-blue)](https://github.com/HighCapable/SweetProperty/blob/master/LICENSE)
![Blank](https://img.shields.io/badge/version-v1.0.0-green)
[![Telegram](https://img.shields.io/badge/Discussion-Telegram-blue.svg?logo=telegram)](https://t.me/HighCapable_Dev)
[![GitHub license](https://img.shields.io/github/license/HighCapable/SweetProperty?color=blue)](https://github.com/HighCapable/SweetProperty/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/v/release/HighCapable/SweetProperty?display_name=release&logo=github&color=green)](https://github.com/HighCapable/SweetProperty/releases)
[![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram)](https://t.me/HighCapable_Dev)
<img src="https://github.com/HighCapable/SweetProperty/blob/master/img-src/icon.png?raw=true" width = "100" height = "100" alt="LOGO"/>

View File

@@ -2,4 +2,18 @@
## 1.0.0 | 2023.09.03
- 首个版本提交至 Maven
- 首个版本提交至 Maven
## 1.0.1 | 2023.09.04
- 新增启用 `isEnableTypeAutoConversion` 后可以使用 '' 或 "" 强制设置一个键值内容为字符串类型
- 修改生成的代码中属性键值的内容使用优化后的类型呈现
## 1.0.2 | 2023.09.07
- 使用 `net.lingala.zip4j` 取代 JDK 默认创建压缩文档功能修复在 Windows 平台中 Gradle 8.0.2+ 版本创建的 JAR 损坏导致找不到生成的 Class 问题
- 重构自动生成代码部分的装载功能,增加可能找不到 Class 的错误提示
- 作废了 ~~`propertiesFileName`~~ 方法
- 新增 `propertiesFileNames` 方法,现在你可以同时设置一组属性配置文件名称了
- 新增 `includeKeys` 方法,现在你可以设置仅包含的属性键值名称数组了
- 新增 `keyValuesRules` 方法,现在你可以在属性键值装载过程中修改键值内容的实际解析结果

View File

@@ -2,4 +2,19 @@
## 1.0.0 | 2023.09.03
- The first version is submitted to Maven
- The first version is submitted to Maven
## 1.0.1 | 2023.09.04
- After enabling `isEnableTypeAutoConversion`, you can use '' or "" to force the content of a value to be a string type
- Modify the content of the properties key-values in the generated code to use the optimized type to render
## 1.0.2 | 2023.09.07
- Use `net.lingala.zip4j` to replace JDK's default function of creating compressed files and fix the problem that the JAR created by Gradle 8.0.2+
version on Windows platform is broken and the generated classes cannot be found
- Refactor the loading function of the automatically generated code part, and add an error message that classes may not be found
- Deprecated ~~`propertiesFileName`~~ method
- Added `propertiesFileNames` method, now you can set a group of properties file names at the same time
- Added `includeKeys` method, now you can set an array of properties key names to include only
- Added `keyValuesRules` method, now you can modify the actual parsing result of the value content during the properties key-values loading process

View File

@@ -70,23 +70,33 @@ sweetProperty {
// 启用功能
// 你可以分别对 "sourcesCode"、"buildScript" 进行设置
isEnable = true
// 设置属性配置文件名称
// 一般情况下不需要修改此设置,错误的文件名将导致获取到空键值内容
// 如果你有一个自定义名称的属性键值文件,你可以修改这里的设置
// 注意:建议为每个项目单独配置,而不是在全局中修改,以防发生问题
propertiesFileName = "gradle.properties"
// 是否启用排除非字符串类型键值内容
// 默认启用,启用后将从属性键值中排除不是字符串类型的键值及内容
// 这可以排除例如一些系统环境变量的配置或内存中的数据
isEnableExcludeNonStringValue = true
// 是否启用类型自动转换功能
// 默认启用,启用后将自动识别属性键值中的类型并转换为对应的类型
// 例如 "name=hello" 和 "number=1" 它们将会被自动转换为 String 和 Int
// 在启用后如果你想要强制设置一个键值内容为字符串类型,你可以使用单引号或双引号包裹整个字符串
// 注意:在关闭此功能后如上所述的功能也将同时失效
// 例如 name=hello 和 number=1 它们将会被自动转换为 String 和 Int
// 例如 stringNumber="1" 或 stringNumber='1' 它们将会被强制转换为 String
isEnableTypeAutoConversion = true
// 是否启用键值内容插值功能
// 默认启用,启用后将自动识别属性键值内容中的 ${...} 内容并进行替换
// 注意:插值的内容仅会从当前 (当前配置文件) 属性键值列表进行查找
isEnableValueInterpolation = true
// 设置属性配置文件名称数组
// 属性配置文件将根据你设置的文件名称自动从当前根项目、子项目以及用户目录的根目录进行获取
// 你可以添加多组属性配置文件名称,将按照顺序依次进行读取
// 一般情况下不需要修改此设置,错误的文件名将导致获取到空键值内容
// 你可以配置 "isAddDefault" 参数来决定是否添加默认的 "gradle.properties" 文件名称
// 如果你有一个或多个自定义名称的属性键值文件,你可以修改这里的设置
// 注意:建议为每个项目单独配置,而不是在全局中修改,以防发生问题
propertiesFileNames(
"some_other_1.properties",
"some_other_2.properties",
isAddDefault = true
)
// 设置固定存在的属性键值数组
// 在这里可以设置一些一定存在的键值,这些键值无论能否从属性键值中得到都会进行生成
// 这些键值在属性键值存在时使用属性键值的内容,不存在时使用这里设置的内容
@@ -108,6 +118,21 @@ sweetProperty {
"exclude.some.key1",
"exclude.some.key2"
)
// 设置需要包含的属性键值名称数组
// 在这里可以设置一些你希望从已知的属性键值中包含的键值名称
// 这些键值在属性键值存在它们时被包含,未被包含的键值不会出现在生成的代码中
// 你可以传入 Regex 或使用 String.toRegex 以使用正则功能
includeKeys(
"include.some.key1",
"include.some.key2"
)
// 设置属性键值规则数组
// 你可以设置一组键值规则,使用 "createValueRule" 创建新的规则,用于解析得到的键值内容
// 这些键值规则在属性键值存在它们时被应用
keyValuesRules(
"some.key1" to createValueRule { if (it.contains("_")) it.replace("_", "-") else it },
"some.key2" to createValueRule { "$it-value" }
)
// 设置从何处生成属性键值
// 默认为 "CURRENT_PROJECT" 和 "ROOT_PROJECT"
// 你可以使用以下类型来进行设置

View File

@@ -73,11 +73,6 @@ sweetProperty {
// Enable functionality
// You can set "sourcesCode" and "buildScript" respectively
isEnable = true
// Set properties name
// In general, you don't need to modify this setting, the wrong file name will lead to getting empty key-values content
// If you have a properties file with a custom name, you can modify the settings here
// Note: It is recommended to configure each project individually, rather than modifying globally, in case of problems
propertiesFileName = "gradle.properties"
// Whether to enable the exclusion of non-string type key-values content
// Enabled by default, when enabled, key-values and content that are not string types will be excluded from the properties key-values
// This can exclude e.g. configuration of some system environment variables or data in memory
@@ -85,14 +80,31 @@ sweetProperty {
// Whether to enable the type automatic conversion function
// Enabled by default, when enabled,
// the type in the properties key-values will be automatically recognized and converted to the corresponding type
// For example "name=hello" and "number=1" they will be automatically converted to String and Int
// After enabling, if you want to force the content of a value to be a string type,
// you can use single quotes or double quotes to wrap the entire string
// Note: After turning off this function, the functions mentioned above will also become invalid at the same time
// For example name=hello and number=1 they will be automatically converted to String and Int
// For example stringNumber="1" or stringNumber='1' they will be coerced to String
isEnableTypeAutoConversion = true
// Whether to enable key-values content interpolation
// Enabled by default, after enabling, the ${...} content in the properties key-values content
// will be automatically recognized and replaced
// Note: The interpolated content will only be searching from the current (current configuration file) properties key-values list
isEnableValueInterpolation = true
// Set a fixed attribute key-value array
// Set properties names array
// The properties file will be automatically obtained from the root directory of
// the current root project, subproject and user directory according to the file name you set
// You can add multiple sets of properties file names, which will be read in order
// In general, you don't need to modify this setting, the wrong file name will lead to getting empty key-values content
// You can configure the "isAddDefault" parameter to decide whether to add the default "gradle.properties" file name
// If you have one or more properties files with custom names, you can modify the settings here
// Note: It is recommended to configure each project individually, rather than modifying globally, in case of problems
propertiesFileNames(
"some_other_1.properties",
"some_other_2.properties",
isAddDefault = true
)
// Set fixed properties key-values array
// Here you can set some key values that must exist,
// and these key values will be generated regardless of whether they can be obtained from the properties key-values
// These key-values use the content of the properties key-values when the properties key-values exists,
@@ -106,7 +118,7 @@ sweetProperty {
"permanent.some.key1" to "some_value_1",
"permanent.some.key2" to "some_value_2"
)
// Set an array of properties key-values names that need to be excluded
// Set properties key-values array names that need to be excluded
// Here you can set some key names that you want to exclude from the known properties keys
// These keys are excluded when they exist in the properties keys and will not appear in the generated code
// Note: If you exclude the key-values set in "permanentKeyValues",
@@ -116,6 +128,22 @@ sweetProperty {
"exclude.some.key1",
"exclude.some.key2"
)
// Set properties key-values array names that need to be included
// Here you can set some key-values names that you want to include from known properties keys
// These keys are included when the properties key-values exists
// Key-values that are not included will not appear in the generated code
// You can pass in a Regex or use String.toRegex to use the regex function
includeKeys(
"include.some.key1",
"include.some.key2"
)
// Set properties key-values rules array
// You can set a set of key-values rules and use "createValueRule" to create new rules for parsing the obtained value content
// These key-values rules are applied when the properties key-values exists
keyValuesRules(
"some.key1" to createValueRule { if (it.contains("_")) it.replace("_", "-") else it },
"some.key2" to createValueRule { "$it-value" }
)
// Set where to generate properties key-values
// Defaults to "CURRENT_PROJECT" and "ROOT_PROJECT"
// You can use the following types to set

View File

@@ -4,7 +4,7 @@ project.description=An easy get project properties anywhere Gradle plugin
project.url=https://github.com/HighCapable/SweetProperty
project.groupName=com.highcapable.sweetproperty
project.moduleName=sweet-property
project.version=1.0.0
project.version=1.0.2
project.licence.name=Apache License 2.0
project.licence.url=https://github.com/HighCapable/SweetProperty/blob/master/LICENSE
project.developer.id=0

View File

@@ -22,4 +22,7 @@ libraries:
kotlinpoet:
version: 1.14.2
javapoet:
version: 1.13.0
version: 1.13.0
net.lingala.zip4j:
zip4j:
version: 2.11.5

View File

@@ -8,8 +8,8 @@ pluginManagement {
}
}
plugins {
id("com.highcapable.sweetdependency") version "1.0.0"
id("com.highcapable.sweetproperty") version "1.0.0"
id("com.highcapable.sweetdependency") version "1.0.1"
id("com.highcapable.sweetproperty") version "1.0.2"
}
sweetDependency {
isEnableVerboseMode = false

View File

@@ -23,6 +23,7 @@ kotlin {
dependencies {
implementation(com.squareup.kotlinpoet)
implementation(com.squareup.javapoet)
implementation(net.lingala.zip4j.zip4j)
}
gradlePlugin {

View File

@@ -67,4 +67,4 @@ internal fun Project.addDependencyToBuildScript(repositoryPath: String, pomData:
* @param name [Class] 完整名称
* @return [Class]
*/
internal fun Project.loadBuildScriptClass(name: String) = buildscript.classLoader.loadClass(name)
internal fun Project.loadBuildScriptClass(name: String) = runCatching { buildscript.classLoader.loadClass(name) }.getOrNull()

View File

@@ -23,7 +23,7 @@ package com.highcapable.sweetproperty.plugin.config.default
import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
import com.highcapable.sweetproperty.plugin.extension.dsl.configure.SweetPropertyConfigureExtension
import com.highcapable.sweetproperty.utils.noBlank
import com.highcapable.sweetproperty.plugin.generator.factory.PropertyValueRule
/**
* 默认配置类实现类
@@ -63,10 +63,10 @@ internal object DefaultConfigs {
get() = selfBase?.isEnable
?: globalBase?.isEnable
?: baseGenerateConfigs(name).isEnable
override val propertiesFileName
get() = selfBase?.propertiesFileName?.noBlank()
?: globalBase?.propertiesFileName?.noBlank()
?: baseGenerateConfigs(name).propertiesFileName
override val propertiesFileNames
get() = selfBase?.propertiesFileNames
?: globalBase?.propertiesFileNames
?: baseGenerateConfigs(name).propertiesFileNames
override val permanentKeyValues
get() = selfBase?.permanentKeyValues
?: globalBase?.permanentKeyValues
@@ -75,6 +75,14 @@ internal object DefaultConfigs {
get() = selfBase?.excludeKeys
?: globalBase?.excludeKeys
?: baseGenerateConfigs(name).excludeKeys
override val includeKeys
get() = selfBase?.includeKeys
?: globalBase?.includeKeys
?: baseGenerateConfigs(name).includeKeys
override val keyValuesRules
get() = selfBase?.keyValuesRules
?: globalBase?.keyValuesRules
?: baseGenerateConfigs(name).keyValuesRules
override val isEnableExcludeNonStringValue
get() = selfBase?.isEnableExcludeNonStringValue
?: globalBase?.isEnableExcludeNonStringValue
@@ -111,10 +119,10 @@ internal object DefaultConfigs {
get() = selfBase?.isEnable
?: globalBase?.isEnable
?: baseGenerateConfigs(name).isEnable
override val propertiesFileName
get() = selfBase?.propertiesFileName?.noBlank()
?: globalBase?.propertiesFileName?.noBlank()
?: baseGenerateConfigs(name).propertiesFileName
override val propertiesFileNames
get() = selfBase?.propertiesFileNames
?: globalBase?.propertiesFileNames
?: baseGenerateConfigs(name).propertiesFileNames
override val permanentKeyValues
get() = selfBase?.permanentKeyValues
?: globalBase?.permanentKeyValues
@@ -123,6 +131,14 @@ internal object DefaultConfigs {
get() = selfBase?.excludeKeys
?: globalBase?.excludeKeys
?: baseGenerateConfigs(name).excludeKeys
override val includeKeys
get() = selfBase?.includeKeys
?: globalBase?.includeKeys
?: baseGenerateConfigs(name).includeKeys
override val keyValuesRules
get() = selfBase?.keyValuesRules
?: globalBase?.keyValuesRules
?: baseGenerateConfigs(name).keyValuesRules
override val isEnableExcludeNonStringValue
get() = selfBase?.isEnableExcludeNonStringValue
?: globalBase?.isEnableExcludeNonStringValue
@@ -149,9 +165,11 @@ internal object DefaultConfigs {
private fun baseGenerateConfigs(name: String) = object : ISweetPropertyConfigs.IBaseGenerateConfigs {
override val name get() = name
override val isEnable get() = true
override val propertiesFileName get() = ISweetPropertyConfigs.DEFAULT_PROPERTIES_FILE_NAME
override val propertiesFileNames get() = mutableListOf(ISweetPropertyConfigs.DEFAULT_PROPERTIES_FILE_NAME)
override val permanentKeyValues get() = mutableMapOf<String, Any>()
override val excludeKeys get() = mutableListOf<Any>()
override val includeKeys get() = mutableListOf<Any>()
override val keyValuesRules get() = mutableMapOf<String, PropertyValueRule>()
override val isEnableExcludeNonStringValue get() = true
override val isEnableTypeAutoConversion get() = true
override val isEnableValueInterpolation get() = true

View File

@@ -92,10 +92,10 @@ private fun SweetPropertyConfigureExtension.SourcesCodeGenerateConfigureExtensio
?: global?.isEnable
?: globalBase?.isEnable
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnable
override val propertiesFileName
get() = this@create.propertiesFileName.noBlank()
?: global?.propertiesFileName?.noBlank()
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).propertiesFileName
override val propertiesFileNames
get() = this@create.propertiesFileNames
?: global?.propertiesFileNames
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).propertiesFileNames
override val permanentKeyValues
get() = this@create.permanentKeyValues
?: global?.permanentKeyValues
@@ -104,6 +104,14 @@ private fun SweetPropertyConfigureExtension.SourcesCodeGenerateConfigureExtensio
get() = this@create.excludeKeys
?: global?.excludeKeys
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).excludeKeys
override val includeKeys
get() = this@create.includeKeys
?: global?.includeKeys
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).includeKeys
override val keyValuesRules
get() = this@create.keyValuesRules
?: global?.keyValuesRules
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).keyValuesRules
override val isEnableExcludeNonStringValue
get() = this@create.isEnableExcludeNonStringValue
?: selfBase?.isEnableExcludeNonStringValue
@@ -155,10 +163,10 @@ private fun SweetPropertyConfigureExtension.BuildScriptGenerateConfigureExtensio
?: global?.isEnable
?: globalBase?.isEnable
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).isEnable
override val propertiesFileName
get() = this@create.propertiesFileName.noBlank()
?: global?.propertiesFileName?.noBlank()
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).propertiesFileName
override val propertiesFileNames
get() = this@create.propertiesFileNames
?: global?.propertiesFileNames
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).propertiesFileNames
override val permanentKeyValues
get() = this@create.permanentKeyValues
?: global?.permanentKeyValues
@@ -167,6 +175,14 @@ private fun SweetPropertyConfigureExtension.BuildScriptGenerateConfigureExtensio
get() = this@create.excludeKeys
?: global?.excludeKeys
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).excludeKeys
override val includeKeys
get() = this@create.includeKeys
?: global?.includeKeys
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).includeKeys
override val keyValuesRules
get() = this@create.keyValuesRules
?: global?.keyValuesRules
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).keyValuesRules
override val isEnableExcludeNonStringValue
get() = this@create.isEnableExcludeNonStringValue
?: selfBase?.isEnableExcludeNonStringValue

View File

@@ -24,6 +24,7 @@ package com.highcapable.sweetproperty.plugin.config.proxy
import com.highcapable.sweetproperty.SweetProperty
import com.highcapable.sweetproperty.generated.SweetPropertyProperties
import com.highcapable.sweetproperty.plugin.config.type.GenerateLocationType
import com.highcapable.sweetproperty.plugin.generator.factory.PropertyValueRule
/**
* [SweetProperty] 配置类接口类
@@ -120,8 +121,8 @@ internal interface ISweetPropertyConfigs {
/** 是否为当前功能生成代码 */
val isEnable: Boolean
/** 属性配置文件名称 */
val propertiesFileName: String
/** 属性配置文件名称数组 */
val propertiesFileNames: MutableList<String>
/** 固定存在的属性键值数组 */
val permanentKeyValues: MutableMap<String, Any>
@@ -129,6 +130,12 @@ internal interface ISweetPropertyConfigs {
/** 被排除的属性键值名称数组 */
val excludeKeys: MutableList<Any>
/** 被包含的属性键值名称数组 */
val includeKeys: MutableList<Any>
/** 属性键值规则数组 */
val keyValuesRules: MutableMap<String, PropertyValueRule>
/** 是否启用排除非字符串类型键值内容 */
val isEnableExcludeNonStringValue: Boolean

View File

@@ -28,6 +28,7 @@ import com.highcapable.sweetproperty.gradle.factory.isUnSafeExtName
import com.highcapable.sweetproperty.plugin.config.factory.create
import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
import com.highcapable.sweetproperty.plugin.config.type.GenerateLocationType
import com.highcapable.sweetproperty.plugin.generator.factory.PropertyValueRule
import com.highcapable.sweetproperty.utils.debug.SError
import com.highcapable.sweetproperty.utils.noEmpty
import org.gradle.api.Action
@@ -200,12 +201,21 @@ open class SweetPropertyConfigureExtension internal constructor() {
*/
open inner class BaseGenerateConfigureExtension internal constructor() {
/** 当前属性配置文件路径数组 */
internal var propertiesFileNames: MutableList<String>? = null
/** 当前固定存在的属性键值数组 */
internal var permanentKeyValues: MutableMap<String, Any>? = null
/** 当前被排除的属性键值名称数组 */
internal var excludeKeys: MutableList<Any>? = null
/** 当前被包含的属性键值名称数组 */
internal var includeKeys: MutableList<Any>? = null
/** 当前属性键值规则数组 */
internal var keyValuesRules: MutableMap<String, PropertyValueRule>? = null
/** 当前生成位置类型数组 */
internal var generateLocationTypes: Array<GenerateLocationType>? = null
@@ -226,10 +236,11 @@ open class SweetPropertyConfigureExtension internal constructor() {
/**
* 设置属性配置文件名称
*
* 默认为 [ISweetPropertyConfigs.DEFAULT_PROPERTIES_FILE_NAME]
* - 此方法已弃用 - 在之后的版本中将直接被删除
*
* - 注意:一般情况下不需要修改此设置 - 错误的文件名将导致获取到空键值内容
* - 请现在迁移到 [propertiesFileNames]
*/
@Deprecated(message = "Migrate to propertiesFileNames(...)")
var propertiesFileName = ""
@JvmName("propertiesFileName") set
@@ -244,8 +255,11 @@ open class SweetPropertyConfigureExtension internal constructor() {
/**
* 是否启用类型自动转换功能
*
*
* 默认启用 - 启用后将自动识别属性键值中的类型并转换为对应的类型
*
* 在启用后如果你想要强制设置一个键值内容为字符串类型 - 你可以使用单引号或双引号包裹整个字符串
*
* 注意:在关闭此功能后如上所述的功能也将同时失效
*/
var isEnableTypeAutoConversion: Boolean? = null
@JvmName("enableTypeAutoConversion") set
@@ -260,6 +274,27 @@ open class SweetPropertyConfigureExtension internal constructor() {
var isEnableValueInterpolation: Boolean? = null
@JvmName("enableValueInterpolation") set
/**
* 设置属性配置文件名称数组
*
* 属性配置文件将根据你设置的文件名称自动从当前根项目、子项目以及用户目录的根目录进行获取
*
* 默认为 [ISweetPropertyConfigs.DEFAULT_PROPERTIES_FILE_NAME]
*
* 你可以添加多组属性配置文件名称 - 将按照顺序依次进行读取
*
* - 注意:一般情况下不需要修改此设置 - 错误的文件名称将导致获取到空键值内容
* @param names 要添加的属性配置文件名称数组
* @param isAddDefault 是否添加默认的属性配置文件名称 - 默认是
*/
@JvmOverloads
fun propertiesFileNames(vararg names: String, isAddDefault: Boolean = true) {
if (names.isEmpty()) SError.make("Properties file names must not be empty")
if (names.any { it.isBlank() }) SError.make("Properties file names must not have blank contents")
propertiesFileNames = names.distinct().toMutableList()
if (isAddDefault) propertiesFileNames?.add(0, ISweetPropertyConfigs.DEFAULT_PROPERTIES_FILE_NAME)
}
/**
* 设置固定存在的属性键值数组
*
@@ -310,6 +345,65 @@ open class SweetPropertyConfigureExtension internal constructor() {
excludeKeys = keys.distinct().toMutableList()
}
/**
* 设置需要包含的属性键值名称数组
*
* 在这里可以设置一些你希望从已知的属性键值中包含的键值名称
*
* 这些键值在属性键值存在它们时被包含 - 未被包含的键值不会出现在生成的代码中
* @param keys 键值名称数组 - 你可以传入 [Regex] 或使用 [String.toRegex] 以使用正则功能
*/
fun includeKeys(vararg keys: Any) {
if (keys.isEmpty()) SError.make("Include keys must not be empty")
if (keys.any { it.toString().isBlank() }) SError.make("Include keys must not have blank contents")
includeKeys = keys.distinct().toMutableList()
}
/**
* 设置属性键值规则数组
*
* 你可以设置一组键值规则 - 使用 [createValueRule] 创建新的规则 - 用于解析得到的键值内容
*
* 示例如下 ↓
*
* ```kotlin
* keyValuesRules(
* "some.key1" to createValueRule { if (it.contains("_")) it.replace("_", "-") else it },
* "some.key2" to createValueRule { "$it-value" }
* )
* ```
*
* 这些键值规则在属性键值存在它们时被应用
* @param pairs 属性键值规则数组
*/
@JvmName("-kotlin-dsl-only-keyValuesRules-")
fun keyValuesRules(vararg pairs: Pair<String, PropertyValueRule>) {
if (pairs.isEmpty()) SError.make("Key-values rules must not be empty")
if (pairs.any { it.first.isBlank() }) SError.make("Key-values rules must not have blank contents")
keyValuesRules = mutableMapOf(*pairs)
}
/**
* 设置属性键值规则数组 (Groovy 兼容方法)
*
* 你可以设置一组键值规则 - 使用 [createValueRule] 创建新的规则 - 用于解析得到的键值内容
*
* 这些键值规则在属性键值存在它们时被应用
* @param rules 属性键值规则数组
*/
fun keyValuesRules(rules: Map<String, PropertyValueRule>) {
if (rules.isEmpty()) SError.make("Key-values rules must not be empty")
if (rules.any { it.key.isBlank() }) SError.make("Key-values rules must not have blank contents")
keyValuesRules = rules.toMutableMap()
}
/**
* 创建新的属性键值规则
* @param rule 回调当前规则
* @return [PropertyValueRule]
*/
fun createValueRule(rule: PropertyValueRule) = rule
/**
* 设置从何处生成属性键值
*

View File

@@ -194,13 +194,13 @@ internal class PropertiesAccessorsGenerator {
*/
private fun TypeSpec.Builder.addFinalValueMethod(accessorsName: String, methodName: String, value: Any) =
addMethod(
MethodSpec.methodBuilder("get${methodName.capitalize()}")
.addJavadoc("Resolve the \"$accessorsName\" value \"$value\"")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL).apply {
val typedValue = value.parseTypedValue(configs.isEnableTypeAutoConversion)
returns(typedValue.first.java)
addStatement("return ${typedValue.second}")
}.build()
MethodSpec.methodBuilder("get${methodName.capitalize()}").apply {
val typedValue = value.parseTypedValue(configs.isEnableTypeAutoConversion)
addJavadoc("Resolve the \"$accessorsName\" value ${typedValue.second}")
addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.returns(typedValue.first.java)
.addStatement("return ${typedValue.second}")
}.build()
)
/**

View File

@@ -70,7 +70,7 @@ internal class PropertiesSourcesGenerator {
keyValues.forEach { (key, value) ->
val typedValue = value.parseTypedValue(configs.isEnableTypeAutoConversion)
addProperty(PropertySpec.builder(key.firstNumberToLetter().underscore(), typedValue.first).apply {
addKdoc("Resolve the \"$key\" value \"$value\"")
addKdoc("Resolve the \"$key\" value ${typedValue.second}")
if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
addModifiers(KModifier.CONST)
initializer(typedValue.second.toKotlinPoetSpace())

View File

@@ -26,19 +26,29 @@ import kotlin.reflect.KClass
/** 属性键值数组类型定义 */
internal typealias PropertyMap = MutableMap<String, Any>
/** 属性键值规则类型定义 */
internal typealias PropertyValueRule = (value: String) -> String
/**
* 解析到键值内容类型
* @param isAutoConversion 是否自动转换类型
* @return [Pair]<[KClass], [String]>
*/
internal fun Any.parseTypedValue(isAutoConversion: Boolean): Pair<KClass<*>, String> {
var isStringType = false
val valueString = toString()
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\\", "\\\\")
.replace("\"", "\\\"")
.let {
if (isAutoConversion && (it.startsWith("\"") && it.endsWith("\"") || it.startsWith("'") && it.endsWith("'"))) {
isStringType = true
it.drop(1).dropLast(1)
} else it.replace("\"", "\\\"")
}
if (isAutoConversion.not()) return Pair(String::class, "\"$valueString\"")
val typeSpec = when {
isStringType -> String::class
valueString.trim().toIntOrNull() != null -> Int::class
valueString.trim().toLongOrNull() != null -> Long::class
valueString.trim().toDoubleOrNull() != null -> Double::class

View File

@@ -45,8 +45,8 @@ import com.highcapable.sweetproperty.utils.hasInterpolation
import com.highcapable.sweetproperty.utils.isEmpty
import com.highcapable.sweetproperty.utils.noBlank
import com.highcapable.sweetproperty.utils.noEmpty
import com.highcapable.sweetproperty.utils.parseFileSeparator
import com.highcapable.sweetproperty.utils.replaceInterpolation
import com.highcapable.sweetproperty.utils.toStringMap
import com.highcapable.sweetproperty.utils.uppercamelcase
import org.gradle.api.DomainObjectCollection
import org.gradle.api.Project
@@ -156,7 +156,10 @@ internal object PropertiesDeployHelper {
allProperties.add(generatedProperties(subConfigs.buildScript, ProjectDescriptor.create(settings, name)))
allConfigs.add(subConfigs.buildScript)
}
if (isConfigsModified.not() && allProperties == cachedSettingsProperties && accessorsDir.isEmpty().not()) return
if (isConfigsModified.not() &&
allProperties == cachedSettingsProperties &&
accessorsDir.resolve(accessorsPomData.relativePomPath).isEmpty().not()
) return
cachedSettingsProperties = allProperties
accessorsGenerator.build(allConfigs, allProperties).compile(accessorsPomData, accessorsDir.absolutePath, accessorsGenerator.compileStubFiles)
}
@@ -166,7 +169,8 @@ internal object PropertiesDeployHelper {
* @param rootProject 当前根项目
*/
private fun resolveAccessors(rootProject: Project) {
if (accessorsDir.isEmpty().not()) rootProject.addDependencyToBuildScript(accessorsDir.absolutePath, accessorsPomData)
if (accessorsDir.resolve(accessorsPomData.relativePomPath).isEmpty().not())
rootProject.addDependencyToBuildScript(accessorsDir.absolutePath, accessorsPomData)
}
/**
@@ -178,7 +182,15 @@ internal object PropertiesDeployHelper {
fun Project.deploy() {
val configs = configs.with(this).buildScript
if (configs.isEnable.not()) return
getOrCreate(configs.extensionName.camelcase(), loadBuildScriptClass(accessorsGenerator.propertiesClass(configs.name)))
val className = accessorsGenerator.propertiesClass(configs.name)
val accessorsClass = loadBuildScriptClass(className) ?: SError.make(
"""
Generated class "$className" not found, stop loading $this
Please check whether the initialization process is interrupted and re-run Gradle Sync
If this doesn't work, please manually delete the entire "${accessorsDir.absolutePath}" directory
""".trimIndent()
)
getOrCreate(configs.extensionName.camelcase(), accessorsClass)
}
rootProject.deploy()
rootProject.subprojects.forEach { it.deploy() }
@@ -244,43 +256,52 @@ internal object PropertiesDeployHelper {
val propteries = mutableMapOf<String, Any>()
configs.permanentKeyValues.forEach { (key, value) -> propteries[key] = value }
configs.generateLocationTypes.forEach {
val nativeKeyValues = when (it) {
GenerateLocationType.CURRENT_PROJECT -> createProperties(configs, descriptor.currentDir)
GenerateLocationType.ROOT_PROJECT -> createProperties(configs, descriptor.rootDir)
GenerateLocationType.GLOBAL -> createProperties(configs, descriptor.homeDir)
GenerateLocationType.SYSTEM -> System.getProperties()
GenerateLocationType.SYSTEM_ENV -> System.getenv()
} ?: emptyMap()
val resolveKeyValues = mutableMapOf<String, Any>()
nativeKeyValues.forEach native@{ (key, value) ->
val hasExcludeKeys = configs.excludeKeys.noEmpty()?.any { content ->
when (content) {
is Regex -> content.toString().isNotBlank() && content.matches(key.toString())
else -> content.toString() == key
}
} ?: false
if (hasExcludeKeys) return@native
val isAvailableKeyValue = if (configs.isEnableExcludeNonStringValue)
mutableMapOf<Any?, Any?>().apply {
when (it) {
GenerateLocationType.CURRENT_PROJECT -> createProperties(configs, descriptor.currentDir).forEach { putAll(it) }
GenerateLocationType.ROOT_PROJECT -> createProperties(configs, descriptor.rootDir).forEach { putAll(it) }
GenerateLocationType.GLOBAL -> createProperties(configs, descriptor.homeDir).forEach { putAll(it) }
GenerateLocationType.SYSTEM -> putAll(System.getProperties())
GenerateLocationType.SYSTEM_ENV -> putAll(System.getenv())
}
}.filter { (key, value) ->
if (configs.isEnableExcludeNonStringValue)
key is CharSequence && key.isNotBlank() && value is CharSequence
else key.toString().isNotBlank() && value != null
if (isAvailableKeyValue) resolveKeyValues[key.toString()] = value
}
resolveKeyValues.forEach { (key, value) ->
val resolveKeys = mutableListOf<String>()
}.toStringMap().filter { (key, _) ->
configs.includeKeys.noEmpty()?.any { content ->
when (content) {
is Regex -> content.matches(key)
else -> content.toString() == key
}
} ?: true
}.filter { (key, _) ->
configs.excludeKeys.noEmpty()?.none { content ->
when (content) {
is Regex -> content.matches(key)
else -> content.toString() == key
}
} ?: true
}.toMutableMap().also { resolveKeyValues ->
resolveKeyValues.onEach { (key, value) ->
val resolveKeys = mutableListOf<String>()
/**
* 处理键值内容
* @return [String]
*/
fun String.resolveValue(): String = replaceInterpolation { matchKey ->
if (resolveKeys.size > 5) SError.make("Key \"$key\" has been called recursively multiple times of those $resolveKeys")
resolveKeys.add(matchKey)
val resolveValue = if (configs.isEnableValueInterpolation) resolveKeyValues[matchKey]?.toString() ?: "" else matchKey
if (resolveValue.hasInterpolation()) resolveValue.resolveValue()
else resolveValue
}
if (value.toString().hasInterpolation()) resolveKeyValues[key] = value.toString().resolveValue()
}; propteries.putAll(resolveKeyValues)
/**
* 处理键值内容
* @return [String]
*/
fun String.resolveValue(): String = replaceInterpolation { matchKey ->
if (resolveKeys.size > 5) SError.make("Key \"$key\" has been called recursively multiple times of those $resolveKeys")
resolveKeys.add(matchKey)
val resolveValue = if (configs.isEnableValueInterpolation) resolveKeyValues[matchKey] ?: "" else matchKey
if (resolveValue.hasInterpolation()) resolveValue.resolveValue()
else resolveValue
}
if (value.hasInterpolation()) resolveKeyValues[key] = value.resolveValue()
}.takeIf { configs.keyValuesRules.isNotEmpty() }?.forEach { (key, value) ->
configs.keyValuesRules[key]?.also { resolveKeyValues[key] = it(value) }
}; propteries.putAll(resolveKeyValues)
}
}; return propteries
}
@@ -320,12 +341,17 @@ internal object PropertiesDeployHelper {
}
/**
* 创建新的 [Properties]
* 创建新的 [Properties] 数组
* @param configs 当前配置
* @param dir 当前目录
* @return [Properties] or null
* @return [MutableList]<[Properties]>
*/
private fun createProperties(configs: ISweetPropertyConfigs.IBaseGenerateConfigs, dir: File?) = runCatching {
Properties().apply { load(FileReader(dir?.resolve(configs.propertiesFileName)?.absolutePath?.parseFileSeparator() ?: "")) }
}.getOrNull()
mutableListOf<Properties>().apply {
configs.propertiesFileNames.forEach {
val propertiesFile = dir?.resolve(it)
if (propertiesFile?.exists() == true) add(Properties().apply { load(FileReader(propertiesFile.absolutePath)) })
}
}
}.getOrNull() ?: mutableListOf()
}

View File

@@ -21,6 +21,12 @@
*/
package com.highcapable.sweetproperty.utils
/**
* 转换当前 [Map] 键值到字符串类型
* @return [Map]<[String], [String]>
*/
internal inline fun <reified K, V> Map<K, V>.toStringMap() = mapKeys { e -> e.key.toString() }.mapValues { e -> e.value.toString() }
/**
* 当数组不为空时返回非空
* @return [T] or null

View File

@@ -24,11 +24,10 @@ package com.highcapable.sweetproperty.utils.code
import com.highcapable.sweetproperty.utils.code.entity.MavenPomData
import com.highcapable.sweetproperty.utils.debug.SError
import com.highcapable.sweetproperty.utils.deleteEmptyRecursively
import com.highcapable.sweetproperty.utils.parseFileSeparator
import com.highcapable.sweetproperty.utils.toFile
import net.lingala.zip4j.ZipFile
import net.lingala.zip4j.model.ZipParameters
import java.io.File
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
import javax.tools.DiagnosticCollector
import javax.tools.JavaFileObject
import javax.tools.StandardLocation
@@ -84,8 +83,8 @@ internal object CodeCompiler {
sourceFile.writeText(it.getCharContent(true).toString())
}
}; outputClassesDir.deleteEmptyRecursively()
writeMetaInf(outputClassesDir.absolutePath)
writeMetaInf(outputSourcesDir.absolutePath)
writeMetaInf(outputClassesDir)
writeMetaInf(outputSourcesDir)
createJarAndPom(pomData, outputDir, outputBuildDir, outputClassesDir, outputSourcesDir)
} else SError.make("Failed to compile java files into path: $outputDirPath\n$diagnosticsMessage")
}
@@ -99,30 +98,29 @@ internal object CodeCompiler {
* @param sourcesDir 编译源码目录
*/
private fun createJarAndPom(pomData: MavenPomData, outputDir: File, buildDir: File, classesDir: File, sourcesDir: File) {
val pomPath = "${pomData.groupId.toPomPathName()}/${pomData.artifactId}/${pomData.version}"
val pomDir = "${outputDir.absolutePath}/$pomPath".toFile().also { if (it.exists().not()) it.mkdirs() }
val pomDir = outputDir.resolve(pomData.relativePomPath).also { if (it.exists().not()) it.mkdirs() }
packageToJar(classesDir, pomDir, pomData, isSourcesJar = false)
packageToJar(sourcesDir, pomDir, pomData, isSourcesJar = true)
writePom(pomDir.absolutePath, pomData)
writePom(pomDir, pomData)
buildDir.deleteRecursively()
}
/**
* 写入 META-INF/MANIFEST.MF
* @param dirPath 当前目录路径
* @param dir 当前目录
*/
private fun writeMetaInf(dirPath: String) {
val metaInfFile = "$dirPath/META-INF".toFile().apply { mkdirs() }
"${metaInfFile.absolutePath}/MANIFEST.MF".toFile().writeText("Manifest-Version: 1.0")
private fun writeMetaInf(dir: File) {
val metaInfDir = dir.resolve("META-INF").apply { mkdirs() }
metaInfDir.resolve("MANIFEST.MF").writeText("Manifest-Version: 1.0")
}
/**
* 写入 POM
* @param dirPath 当前目录路径
* @param dir 当前目录
* @param pomData Maven POM 实体
*/
private fun writePom(dirPath: String, pomData: MavenPomData) =
"$dirPath/${pomData.artifactId}-${pomData.version}.pom".toFile().writeText(
private fun writePom(dir: File, pomData: MavenPomData) =
dir.resolve("${pomData.artifactId}-${pomData.version}.pom").writeText(
"""
<?xml version="1.0" encoding="UTF-8"?>
<project
@@ -137,12 +135,6 @@ internal object CodeCompiler {
""".trimIndent()
)
/**
* 转换到 [MavenPomData] 目录名称
* @return [String]
*/
private fun String.toPomPathName() = trim().replace(".", "/").replace("_", "/").replace(":", "/").replace("-", "/")
/**
* 转换到文件
* @param outputDir 输出目录
@@ -167,26 +159,8 @@ internal object CodeCompiler {
*/
private fun packageToJar(buildDir: File, outputDir: File, pomData: MavenPomData, isSourcesJar: Boolean) {
if (buildDir.exists().not()) SError.make("Jar file output path not found: ${buildDir.absolutePath}")
/**
* 添加文件到 JAR
* @param jos 当前输出流
* @param parentPath 父级路径
*/
fun File.addToJar(jos: JarOutputStream, parentPath: String = "") {
val currentPath = "$parentPath$name".replace("${buildDir.name}|", "").replace("|", "/").parseFileSeparator()
if (isFile) {
if (name.startsWith(".")) return
jos.putNextEntry(JarEntry(currentPath))
inputStream().use { fis ->
val buffer = ByteArray(4096)
var bytesRead: Int
while (fis.read(buffer).also { bytesRead = it } != -1) jos.write(buffer, 0, bytesRead)
}
jos.closeEntry()
} else listFiles()?.forEach { it.addToJar(jos, parentPath = "$currentPath|") }
}
val jarFile = "${outputDir.absolutePath}/${pomData.artifactId}-${pomData.version}${if (isSourcesJar) "-sources" else ""}.jar".toFile()
val jarFile = outputDir.resolve("${pomData.artifactId}-${pomData.version}${if (isSourcesJar) "-sources" else ""}.jar")
if (jarFile.exists()) jarFile.delete()
jarFile.outputStream().use { fos -> JarOutputStream(fos).use { jos -> buildDir.addToJar(jos) } }
ZipFile(jarFile).addFolder(buildDir, ZipParameters().apply { isIncludeRootFolder = false })
}
}

View File

@@ -27,4 +27,17 @@ package com.highcapable.sweetproperty.utils.code.entity
* @param artifactId Artifact Id
* @param version 版本
*/
internal data class MavenPomData(internal val groupId: String, internal val artifactId: String, internal val version: String)
internal data class MavenPomData(internal val groupId: String, internal val artifactId: String, internal val version: String) {
/**
* 获取 [MavenPomData] 相对路径
* @return [String]
*/
internal val relativePomPath get() = "${groupId.toPomPathName()}/$artifactId/$version"
/**
* 转换到 [MavenPomData] 目录名称
* @return [String]
*/
private fun String.toPomPathName() = trim().replace(".", "/").replace("_", "/").replace(":", "/").replace("-", "/")
}