Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
梦境迷离
Scala Macro Tools
提交
9179156e
S
Scala Macro Tools
项目概览
梦境迷离
/
Scala Macro Tools
上一次同步 1 年多
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
Scala Macro Tools
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
未验证
提交
9179156e
编写于
6月 28, 2022
作者:
梦境迷离
提交者:
GitHub
6月 28, 2022
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
support options (#210)
* support options
上级
a0165f67
变更
15
隐藏空白更改
内联
并排
Showing
15 changed file
with
536 addition
and
183 deletion
+536
-183
smt-common/src/main/scala/org/bitlap/common/CaseClassExtractor.scala
...src/main/scala/org/bitlap/common/CaseClassExtractor.scala
+3
-31
smt-common/src/main/scala/org/bitlap/common/CaseClassField.scala
...mon/src/main/scala/org/bitlap/common/CaseClassField.scala
+2
-61
smt-common/src/main/scala/org/bitlap/common/MacroCache.scala
smt-common/src/main/scala/org/bitlap/common/MacroCache.scala
+2
-0
smt-common/src/main/scala/org/bitlap/common/Options.scala
smt-common/src/main/scala/org/bitlap/common/Options.scala
+40
-0
smt-common/src/main/scala/org/bitlap/common/Transformable.scala
...mmon/src/main/scala/org/bitlap/common/Transformable.scala
+35
-6
smt-common/src/main/scala/org/bitlap/common/internal/AbstractMacroProcessor.scala
...a/org/bitlap/common/internal/AbstractMacroProcessor.scala
+47
-34
smt-common/src/main/scala/org/bitlap/common/internal/CaseClassExtractorMacro.scala
.../org/bitlap/common/internal/CaseClassExtractorMacro.scala
+57
-0
smt-common/src/main/scala/org/bitlap/common/internal/CaseClassFieldMacro.scala
...cala/org/bitlap/common/internal/CaseClassFieldMacro.scala
+97
-0
smt-common/src/main/scala/org/bitlap/common/internal/TransformerMacro.scala
...n/scala/org/bitlap/common/internal/TransformerMacro.scala
+121
-45
smt-common/src/test/scala/org/bitlap/common/TransformableTest.scala
.../src/test/scala/org/bitlap/common/TransformableTest.scala
+124
-0
smt-csv-derive/src/main/scala/org/bitlap/csv/derive/DeriveCsvConverter.scala
...main/scala/org/bitlap/csv/derive/DeriveCsvConverter.scala
+1
-1
smt-csv/src/main/scala/org/bitlap/csv/macros/DeriveCsvableBuilder.scala
...in/scala/org/bitlap/csv/macros/DeriveCsvableBuilder.scala
+2
-1
smt-csv/src/main/scala/org/bitlap/csv/macros/DeriveScalableBuilder.scala
...n/scala/org/bitlap/csv/macros/DeriveScalableBuilder.scala
+3
-2
smt-csv/src/main/scala/org/bitlap/csv/macros/DeriveToCaseClass.scala
.../main/scala/org/bitlap/csv/macros/DeriveToCaseClass.scala
+1
-1
smt-csv/src/main/scala/org/bitlap/csv/macros/DeriveToString.scala
...src/main/scala/org/bitlap/csv/macros/DeriveToString.scala
+1
-1
未找到文件。
smt-common/src/main/scala/org/bitlap/common/CaseClassExtractor.scala
浏览文件 @
9179156e
...
...
@@ -21,9 +21,8 @@
package
org.bitlap.common
import
java.time.ZonedDateTime
import
java.time.format.DateTimeFormatter
import
scala.reflect.macros.whitebox
import
org.bitlap.common.internal.CaseClassExtractorMacro
import
scala.reflect.ClassTag
import
scala.reflect.runtime.
{
universe
=>
ru
}
import
scala.reflect.runtime.universe._
...
...
@@ -38,34 +37,7 @@ object CaseClassExtractor {
/** Using the characteristics of the product type to get the field value should force the conversion externally
* (safely).
*/
def
ofValue
[
T
<:
Product
](
t
:
T
,
field
:
CaseClassField
)
:
Option
[
Any
]
=
macro
macroImpl
[
T
]
def
macroImpl
[
T:
c.WeakTypeTag
](
c
:
whitebox.Context
)(
t
:
c.Expr
[
T
],
field
:
c.Expr
[
CaseClassField
])
:
c.Expr
[
Option
[
Any
]]
=
{
import
c.universe._
// scalafmt: { maxColumn = 400 }
val
tree
=
q
"""
if ($t == null) None else {
val _field = $field
_field.${TermName(CaseClassField.fieldNamesTermName)}.find(kv => kv._2 == _field.${TermName(CaseClassField.stringifyTermName)})
.map(kv => $t.productElement(kv._1))
}
"""
exprPrintTree
[
Option
[
Any
]](
c
)(
tree
)
}
def
exprPrintTree
[
Field:
c.WeakTypeTag
](
c
:
whitebox.Context
)(
resTree
:
c.Tree
)
:
c.Expr
[
Field
]
=
{
c
.
info
(
c
.
enclosingPosition
,
s
"\n###### Time: ${ZonedDateTime.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME)} Expanded macro start ######\n"
+
resTree
.
toString
()
+
"\n###### Expanded macro end ######\n"
,
force
=
false
)
c
.
Expr
[
Field
](
resTree
)
}
def
ofValue
[
T
<:
Product
](
t
:
T
,
field
:
CaseClassField
)
:
Option
[
Any
]
=
macro
CaseClassExtractorMacro
.
macroImpl
[
T
]
/** Using scala reflect to get the field value (safely).
*/
...
...
smt-common/src/main/scala/org/bitlap/common/CaseClassField.scala
浏览文件 @
9179156e
...
...
@@ -21,10 +21,7 @@
package
org.bitlap.common
import
org.bitlap.common.CaseClassExtractor.exprPrintTree
import
scala.reflect.macros.whitebox
import
scala.collection.Seq
import
org.bitlap.common.internal.CaseClassFieldMacro
trait
CaseClassField
{
...
...
@@ -44,61 +41,5 @@ object CaseClassField {
final
val
fieldTermName
=
"Field"
final
val
fieldNamesTermName
=
"fieldIndexNames"
def
apply
[
T
<:
Product
](
field
:
T
=>
Any
)
:
CaseClassField
=
macro
selectFieldMacroImpl
[
T
]
def
selectFieldMacroImpl
[
T:
c.WeakTypeTag
](
c
:
whitebox.Context
)(
field
:
c.Expr
[
T
=>
Any
])
:
c.Expr
[
CaseClassField
]
=
{
import
c.universe._
val
packageName
=
q
"_root_.org.bitlap.common"
val
Function
(
_
,
Select
(
_
,
termName
))
=
field
.
tree
val
caseClassParams
=
getCaseClassParams
[
T
](
c
)
val
fieldName
=
termName
.
decodedName
.
toString
val
searchField
=
caseClassParams
.
find
(
_
.
name
.
toTermName
.
decodedName
.
toString
==
fieldName
)
val
fieldType
=
searchField
.
map
(
f
=>
c
.
typecheck
(
tq
"$f"
,
c
.
TYPEmode
).
tpe
)
if
(
searchField
.
isEmpty
||
fieldType
.
isEmpty
)
{
c
.
abort
(
c
.
enclosingPosition
,
s
"""Field name is invalid, "${c.weakTypeOf[T].resultType}" does not have a field named $fieldName!
|Please consider using "CaseClassField[T]($fieldName)" instead of "CaseClassField($fieldName)" """
.
stripMargin
)
}
val
genericType
=
fieldType
.
get
match
{
case
t
if
t
<:<
typeOf
[
Option
[
_
]]
=>
val
genericType
=
t
.
dealias
.
typeArgs
.
head
tq
"_root_.scala.Option[$genericType]"
case
t
if
t
<:<
typeOf
[
Seq
[
_
]]
=>
val
genericType
=
t
.
dealias
.
typeArgs
.
head
tq
"_root_.scala.Seq[$genericType]"
case
t
if
t
<:<
typeOf
[
List
[
_
]]
=>
val
genericType
=
t
.
dealias
.
typeArgs
.
head
tq
"_root_.scala.List[$genericType]"
case
t
=>
tq
"$t"
}
val
fieldNameTypeName
=
TermName
(
s
"${CaseClassField.classNameTermName}$$$fieldName"
)
val
res
=
q
"""
case object $fieldNameTypeName extends $packageName.${TypeName(CaseClassField.classNameTermName)} {
override def ${TermName(CaseClassField.stringifyTermName)}: String = $fieldName
override type ${TypeName(CaseClassField.fieldTermName)} = $genericType
override val ${TermName(CaseClassField.fieldNamesTermName)} =
(${caseClassParams.indices.toList} zip ${caseClassParams.map(_.name.decodedName.toString)}).toMap
}
$fieldNameTypeName
"""
exprPrintTree
[
CaseClassField
](
c
)(
res
)
}
def
getCaseClassParams
[
T:
c.WeakTypeTag
](
c
:
whitebox.Context
)
:
List
[
c.Symbol
]
=
{
import
c.universe._
val
parameters
=
c
.
weakTypeOf
[
T
].
resultType
.
member
(
TermName
(
"<init>"
)).
typeSignature
.
paramLists
if
(
parameters
.
size
>
1
)
{
c
.
abort
(
c
.
enclosingPosition
,
"The constructor of case class has currying!"
)
}
parameters
.
flatten
}
def
apply
[
T
<:
Product
](
field
:
T
=>
Any
)
:
CaseClassField
=
macro
CaseClassFieldMacro
.
selectFieldMacroImpl
[
T
]
}
smt-common/src/main/scala/org/bitlap/common/MacroCache.scala
浏览文件 @
9179156e
...
...
@@ -46,4 +46,6 @@ object MacroCache {
lazy
val
classFieldTypeMapping
:
mutable.Map
[
Int
,
mutable.Map
[
String
,
Any
]]
=
mutable
.
Map
.
empty
lazy
val
classFieldDefaultValueMapping
:
mutable.Map
[
Int
,
mutable.Map
[
String
,
Any
]]
=
mutable
.
Map
.
empty
lazy
val
transformerOptionsMapping
:
mutable.Map
[
Int
,
mutable.Set
[
Options
]]
=
mutable
.
Map
.
empty
}
smt-common/src/main/scala/org/bitlap/common/Options.scala
0 → 100644
浏览文件 @
9179156e
/*
* Copyright (c) 2022 bitlap
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package
org.bitlap.common
/** @author
* 梦境迷离
* @version 1.0,6/27/22
*/
sealed
trait
Options
object
Options
{
case
object
enableOptionDefaultsToNone
extends
Options
case
object
enableCollectionDefaultsToEmpty
extends
Options
case
object
disableCollectionDefaultsToEmpty
extends
Options
case
object
disableOptionDefaultsToNone
extends
Options
}
smt-common/src/main/scala/org/bitlap/common/Transformable.scala
浏览文件 @
9179156e
...
...
@@ -20,6 +20,7 @@
*/
package
org.bitlap.common
import
org.bitlap.common.internal.TransformerMacro
/** @author
* 梦境迷离
...
...
@@ -27,12 +28,15 @@ package org.bitlap.common
*/
class
Transformable
[
From
,
To
]
{
/** @param selectFromField
/** Sets the `From` to `To` mapping relationship of the field type.
*
* When the map function returns a known constant value, it means that the type mapping becomes a set value.
*
* @param selectFromField
* Select the name of the field to be mapped in the `From` class.
* @param map
* Specify the type mapping of the field, which must be provided when the type is incompatible, or else attempt to
* search for an implicit `Transformer[FromField, ToField]` (a failed search will result in a compile failure).
*
* @tparam FromField
* field type
* @tparam ToField
...
...
@@ -47,7 +51,9 @@ class Transformable[From, To] {
)
:
Transformable
[
From
,
To
]
=
macro
TransformerMacro
.
mapTypeImpl
[
From
,
To
,
FromField
,
ToField
]
/** @param selectFromField
/** Sets the `From` to `To` mapping relationship of the field name.
*
* @param selectFromField
* Select the name of the field to be mapped in the `From` class.
* @param selectToField
* Select the name of the field to be mapped in the `To` class.
...
...
@@ -66,14 +72,36 @@ class Transformable[From, To] {
)
:
Transformable
[
From
,
To
]
=
macro
TransformerMacro
.
mapNameImpl
[
From
,
To
,
FromField
,
ToField
]
/** Defines default value for missing field to successfully create `To` object. This method has the lowest priority.
/** Defines a default value for missing field to successfully create `To` object. This method has a higher priority
* than `enableOptionDefaultsToNone` or `enableCollectionDefaultsToEmpty`.
*
* Only the `selectToField` field does not have the same name found in the `From` and is not in the name mapping.
* So, even if `enableCollectionDefaultsToEmpty` or `enableCollectionDefaultsToEmpty`, you can also use
* `setDefaultValue` method to set the initial value for a single field.
*/
@unchecked
def
setDefaultValue
[
ToField
](
selectToField
:
To
=>
ToField
,
defaultValue
:
ToField
)
:
Transformable
[
From
,
To
]
=
macro
TransformerMacro
.
setDefaultValueImpl
[
From
,
To
,
ToField
]
/** Sets target value of optional fields to `None` if field is missing from source type `From`.
*/
def
enableOptionDefaultsToNone
:
Transformable
[
From
,
To
]
=
macro
TransformerMacro
.
enableOptionDefaultsToNoneImpl
[
From
,
To
]
/** Sets target value of collection fields to `empty` if field is missing from source type `From`.
*/
def
enableCollectionDefaultsToEmpty
:
Transformable
[
From
,
To
]
=
macro
TransformerMacro
.
enableCollectionDefaultsToEmptyImpl
[
From
,
To
]
/** Disable `None` fallback value for optional fields in `To`. This is the default configuration option.
*/
def
disableOptionDefaultsToNone
:
Transformable
[
From
,
To
]
=
macro
TransformerMacro
.
disableOptionDefaultsToNoneImpl
[
From
,
To
]
/** Disable `empty` fallback value for collection fields in `To`. Support List, Seq, Vector, Set. This is the default
* configuration option.
*/
def
disableCollectionDefaultsToEmpty
:
Transformable
[
From
,
To
]
=
macro
TransformerMacro
.
disableCollectionDefaultsToEmptyImpl
[
From
,
To
]
def
instance
:
Transformer
[
From
,
To
]
=
macro
TransformerMacro
.
instanceImpl
[
From
,
To
]
}
...
...
@@ -82,6 +110,7 @@ object Transformable {
/** Automatically derive `Transformable[From, To]` for case classes only, for non-case classes you should use the
* `setType` method to configure the mapping relationship.
*
* @tparam From
* @tparam To
* @return
...
...
smt-common/src/main/scala/org/bitlap/common/AbstractMacroProcessor.scala
→
smt-common/src/main/scala/org/bitlap/common/
internal/
AbstractMacroProcessor.scala
浏览文件 @
9179156e
...
...
@@ -19,7 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package
org.bitlap.common
package
org.bitlap.common
.internal
import
java.time.ZonedDateTime
import
java.time.format.DateTimeFormatter
...
...
@@ -44,7 +44,11 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
isOption
:
Boolean
=
false
,
isVector
:
Boolean
=
false
,
isSet
:
Boolean
=
false
)
)
{
def
isCollection
:
Boolean
=
isSeq
||
isList
||
isOption
||
isVector
||
isSet
def
isStrictCollection
:
Boolean
=
isSeq
||
isList
||
isVector
||
isSet
}
final
case
class
FieldTreeInformation
(
index
:
Int
,
...
...
@@ -59,7 +63,9 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
fieldName
:
String
,
fieldType
:
Type
,
collectionFlags
:
CollectionFlags
,
genericType
:
List
[
Type
]
=
Nil
genericType
:
List
[
Type
]
=
Nil
,
hasDefaultValue
:
Boolean
,
zeroValue
:
Tree
)
def
tryGetOrElse
(
tree
:
Tree
,
default
:
Tree
)
:
Tree
=
...
...
@@ -82,7 +88,7 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
*/
def
checkGetFieldTreeInformationList
[
T:
WeakTypeTag
](
columnsFunc
:
TermName
)
:
List
[
FieldTreeInformation
]
=
{
val
idxColumn
=
(
i
:
Int
)
=>
q
"$columnsFunc()($i)"
val
params
=
getCaseClassFieldInfo
[
T
]()
val
params
=
getCaseClassFieldInfo
List
[
T
]()
val
paramsSize
=
params
.
size
val
types
=
params
.
map
(
_
.
fieldType
)
val
indexColumns
=
(
0
until
paramsSize
).
toList
.
map
(
i
=>
i
->
idxColumn
(
i
))
...
...
@@ -91,46 +97,65 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
}
indexColumns
zip
types
map
{
kv
=>
val
(
isOption
,
isSeq
,
isList
,
isVector
,
isSet
)
=
isWrapType
(
kv
.
_2
)
val
typed
=
c
.
typecheck
(
tq
"${kv._2}"
,
c
.
TYPEmode
).
tpe
var
genericType
:
List
[
Type
]
=
Nil
if
(
isList
||
isSeq
||
isOption
||
isVector
||
isSet
)
{
val
collectionFlag
=
isWrapType
(
kv
.
_2
)
val
typed
=
c
.
typecheck
(
tq
"${kv._2}"
,
c
.
TYPEmode
).
tpe
var
genericType
:
List
[
Type
]
=
Nil
if
(
collectionFlag
.
isCollection
)
{
genericType
=
typed
.
dealias
.
typeArgs
:::
genericType
}
FieldTreeInformation
(
kv
.
_1
.
_1
,
kv
.
_1
.
_2
,
kv
.
_2
,
get
Default
Value
(
kv
.
_2
),
CollectionFlags
(
isSeq
,
isList
,
isOption
,
isVector
,
isSet
)
,
get
Zero
Value
(
kv
.
_2
),
collectionFlag
,
genericType
)
}
}
def
getFieldDefaultValueMap
[
T:
WeakTypeTag
](
init
:
MethodSymbol
)
:
Map
[
String
,
Tree
]
=
{
val
classSym
=
weakTypeOf
[
T
].
typeSymbol
init
.
paramLists
.
head
.
map
(
_
.
asTerm
)
.
zipWithIndex
.
flatMap
{
case
(
p
,
i
)
=>
if
(!
p
.
isParamWithDefault
)
None
else
{
val
getterName
=
TermName
(
"apply$default$"
+
(
i
+
1
))
Some
(
p
.
name
.
decodedName
.
toString
->
q
"${classSym.name.toTermName}.$getterName"
)
// moduleSym is none
}
}
.
toMap
}
/** Get only the symbol of the case class constructor parameters.
*
* @tparam T
* Type of the case class.
* @return
*/
def
getCaseClassFieldInfo
[
T:
WeakTypeTag
]()
:
List
[
FieldInformation
]
=
{
val
parameters
=
resolveParameters
[
T
]
def
getCaseClassFieldInfoList
[
T:
WeakTypeTag
]()
:
List
[
FieldInformation
]
=
{
val
init
=
c
.
weakTypeOf
[
T
].
resultType
.
member
(
TermName
(
"<init>"
)).
asMethod
val
defaultValuesTerm
=
getFieldDefaultValueMap
[
T
](
init
)
val
parameters
=
init
.
typeSignature
.
paramLists
if
(
parameters
.
size
>
1
)
{
c
.
abort
(
c
.
enclosingPosition
,
"The constructor of case class has currying!"
)
}
parameters
.
flatten
.
map
{
p
=>
val
typed
=
c
.
typecheck
(
tq
"$p"
,
c
.
TYPEmode
).
tpe
var
genericType
:
List
[
Type
]
=
Nil
val
(
isOption
,
isSeq
,
isList
,
isVector
,
isSet
)
=
isWrapType
(
typed
)
if
(
isList
||
isSeq
||
isOption
||
isVector
||
isSet
)
{
val
typed
=
c
.
typecheck
(
tq
"$p"
,
c
.
TYPEmode
).
tpe
var
genericType
:
List
[
Type
]
=
Nil
val
collectionFlags
=
isWrapType
(
typed
)
if
(
collectionFlags
.
isCollection
)
{
genericType
=
typed
.
dealias
.
typeArgs
:::
genericType
}
FieldInformation
(
p
.
name
.
decodedName
.
toString
,
typed
,
CollectionFlags
(
isSeq
,
isList
,
isOption
,
isVector
,
isSet
),
genericType
collectionFlags
,
genericType
,
defaultValuesTerm
.
contains
(
p
.
name
.
decodedName
.
toString
),
getZeroValue
(
typed
)
)
}
}
...
...
@@ -152,16 +177,6 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
c
.
Expr
[
T
](
resTree
)
}
/** Get the constructor symbol of the case class.
*
* @tparam T
* Type of the case class.
* @return
* The parameters may be currying, so it's a two-level list.
*/
def
resolveParameters
[
T:
WeakTypeTag
]
:
List
[
List
[
Symbol
]]
=
c
.
weakTypeOf
[
T
].
resultType
.
member
(
TermName
(
"<init>"
)).
typeSignature
.
paramLists
/** Get the `TypeName` of the class.
*
* @tparam T
...
...
@@ -179,7 +194,7 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
* @return
*/
def
checkGetFieldZipInformation
[
T:
WeakTypeTag
]
:
FieldZipInformation
=
{
val
params
=
getCaseClassFieldInfo
[
T
]()
val
params
=
getCaseClassFieldInfo
List
[
T
]()
val
paramsSize
=
params
.
size
val
names
=
params
.
map
(
_
.
fieldName
)
FieldZipInformation
(
...
...
@@ -196,7 +211,7 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
def
getBuilderId
(
annoBuilderPrefix
:
String
)
:
Int
=
c
.
prefix
.
actualType
.
toString
.
replace
(
annoBuilderPrefix
,
""
).
toInt
private
def
getDefault
Value
(
typ
:
Type
)
:
Tree
=
def
getZero
Value
(
typ
:
Type
)
:
Tree
=
typ
match
{
case
t
if
t
=:=
typeOf
[
Int
]
=>
q
"0"
...
...
@@ -226,9 +241,7 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
q
"null"
}
private
type
OptionSeqListVectorSet
=
(
Boolean
,
Boolean
,
Boolean
,
Boolean
,
Boolean
)
private
def
isWrapType
(
typed
:
Type
)
:
OptionSeqListVectorSet
=
{
private
def
isWrapType
(
typed
:
Type
)
:
CollectionFlags
=
{
var
isList
:
Boolean
=
false
var
isSeq
:
Boolean
=
false
var
isOption
:
Boolean
=
false
...
...
@@ -247,7 +260,7 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
isSeq
=
true
case
_
=>
}
Tuple5
(
isOption
,
isSeq
,
isList
,
isVector
,
isSet
)
CollectionFlags
(
isSeq
,
isList
,
isOption
,
isVector
,
isSet
)
}
}
smt-common/src/main/scala/org/bitlap/common/internal/CaseClassExtractorMacro.scala
0 → 100644
浏览文件 @
9179156e
/*
* Copyright (c) 2022 bitlap
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package
org.bitlap.common.internal
import
org.bitlap.common.CaseClassField
import
java.time.ZonedDateTime
import
java.time.format.DateTimeFormatter
import
scala.reflect.macros.whitebox
/** @author
* 梦境迷离
* @version 1.0,6/27/22
*/
object
CaseClassExtractorMacro
{
def
macroImpl
[
T
](
c
:
whitebox.Context
)(
t
:
c.Expr
[
T
],
field
:
c.Expr
[
CaseClassField
])
:
c.Expr
[
Option
[
Any
]]
=
{
import
c.universe._
// scalafmt: { maxColumn = 400 }
val
tree
=
q
"""
if ($t == null) None else {
val _field = $field
_field.${TermName(CaseClassField.fieldNamesTermName)}.find(kv => kv._2 == _field.${TermName(CaseClassField.stringifyTermName)})
.map(kv => $t.productElement(kv._1))
}
"""
c
.
info
(
c
.
enclosingPosition
,
s
"\n###### Time: ${ZonedDateTime.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME)} Expanded macro start ######\n"
+
tree
.
toString
()
+
"\n###### Expanded macro end ######\n"
,
force
=
false
)
c
.
Expr
[
Option
[
Any
]](
tree
)
}
}
smt-common/src/main/scala/org/bitlap/common/internal/CaseClassFieldMacro.scala
0 → 100644
浏览文件 @
9179156e
/*
* Copyright (c) 2022 bitlap
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package
org.bitlap.common.internal
import
org.bitlap.common.CaseClassField
import
scala.collection.Seq
import
scala.reflect.macros.whitebox
import
java.time.format.DateTimeFormatter
import
java.time.ZonedDateTime
/** @author
* 梦境迷离
* @version 1.0,6/27/22
*/
object
CaseClassFieldMacro
{
def
selectFieldMacroImpl
[
T:
c.WeakTypeTag
](
c
:
whitebox.Context
)(
field
:
c.Expr
[
T
=>
Any
])
:
c.Expr
[
CaseClassField
]
=
{
import
c.universe._
val
packageName
=
q
"_root_.org.bitlap.common"
val
Function
(
_
,
Select
(
_
,
termName
))
=
field
.
tree
val
caseClassParams
=
getCaseClassParams
[
T
](
c
)
val
fieldName
=
termName
.
decodedName
.
toString
val
searchField
=
caseClassParams
.
find
(
_
.
name
.
toTermName
.
decodedName
.
toString
==
fieldName
)
val
fieldType
=
searchField
.
map
(
f
=>
c
.
typecheck
(
tq
"$f"
,
c
.
TYPEmode
).
tpe
)
if
(
searchField
.
isEmpty
||
fieldType
.
isEmpty
)
{
c
.
abort
(
c
.
enclosingPosition
,
s
"""Field name is invalid, "${c.weakTypeOf[T].resultType}" does not have a field named $fieldName!
|Please consider using "CaseClassField[T]($fieldName)" instead of "CaseClassField($fieldName)" """
.
stripMargin
)
}
val
genericType
=
fieldType
.
get
match
{
case
t
if
t
<:<
typeOf
[
Option
[
_
]]
=>
val
genericType
=
t
.
dealias
.
typeArgs
.
head
tq
"_root_.scala.Option[$genericType]"
case
t
if
t
<:<
typeOf
[
Seq
[
_
]]
=>
val
genericType
=
t
.
dealias
.
typeArgs
.
head
tq
"_root_.scala.Seq[$genericType]"
case
t
if
t
<:<
typeOf
[
List
[
_
]]
=>
val
genericType
=
t
.
dealias
.
typeArgs
.
head
tq
"_root_.scala.List[$genericType]"
case
t
=>
tq
"$t"
}
val
fieldNameTypeName
=
TermName
(
s
"${CaseClassField.classNameTermName}$$$fieldName"
)
val
res
=
q
"""
case object $fieldNameTypeName extends $packageName.${TypeName(CaseClassField.classNameTermName)} {
override def ${TermName(CaseClassField.stringifyTermName)}: String = $fieldName
override type ${TypeName(CaseClassField.fieldTermName)} = $genericType
override val ${TermName(CaseClassField.fieldNamesTermName)} =
(${caseClassParams.indices.toList} zip ${caseClassParams.map(_.name.decodedName.toString)}).toMap
}
$fieldNameTypeName
"""
c
.
info
(
c
.
enclosingPosition
,
s
"\n###### Time: ${ZonedDateTime.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME)} Expanded macro start ######\n"
+
res
.
toString
()
+
"\n###### Expanded macro end ######\n"
,
force
=
false
)
c
.
Expr
[
CaseClassField
](
res
)
}
def
getCaseClassParams
[
T:
c.WeakTypeTag
](
c
:
whitebox.Context
)
:
List
[
c.Symbol
]
=
{
import
c.universe._
val
parameters
=
c
.
weakTypeOf
[
T
].
resultType
.
member
(
TermName
(
"<init>"
)).
typeSignature
.
paramLists
if
(
parameters
.
size
>
1
)
{
c
.
abort
(
c
.
enclosingPosition
,
"The constructor of case class has currying!"
)
}
parameters
.
flatten
}
}
smt-common/src/main/scala/org/bitlap/common/TransformerMacro.scala
→
smt-common/src/main/scala/org/bitlap/common/
internal/
TransformerMacro.scala
浏览文件 @
9179156e
...
...
@@ -19,9 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package
org.bitlap.common
package
org.bitlap.common.internal
import
org.bitlap.common.
{
MacroCache
,
Options
,
Transformable
,
Transformer
=>
BitlapTransformer
}
import
org.bitlap.common.
{
Transformer
=>
BitlapTransformer
}
import
scala.collection.mutable
import
scala.reflect.macros.whitebox
...
...
@@ -33,8 +34,6 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
import
c.universe._
import
scala.collection.immutable
protected
val
packageName
=
q
"_root_.org.bitlap.common"
private
val
builderFunctionPrefix
=
"_TransformableFunction$"
private
val
builderDefaultValuePrefix$
=
"_TransformableDefaultValue$"
...
...
@@ -77,7 +76,54 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
MacroCache
.
classFieldNameMapping
.
getOrElseUpdate
(
builderId
,
mutable
.
Map
.
empty
)
.
update
(
toName
.
decodedName
.
toString
,
fromName
.
decodedName
.
toString
)
val
tree
=
q
"new ${c.prefix.actualType}"
exprPrintTree
[
Transformable
[
From
,
To
]](
force
=
false
,
tree
)
}
def
enableOptionDefaultsToNoneImpl
[
From
,
To
]
=
setOptions
[
From
,
To
](
MacroCache
.
transformerOptionsMapping
.
getOrElseUpdate
(
_
,
mutable
.
Set
.
empty
)
.
add
(
Options
.
enableOptionDefaultsToNone
),
MacroCache
.
transformerOptionsMapping
.
getOrElseUpdate
(
_
,
mutable
.
Set
.
empty
)
.
remove
(
Options
.
disableOptionDefaultsToNone
)
)
def
enableCollectionDefaultsToEmptyImpl
[
From
,
To
]
=
setOptions
[
From
,
To
](
MacroCache
.
transformerOptionsMapping
.
getOrElseUpdate
(
_
,
mutable
.
Set
.
empty
)
.
add
(
Options
.
enableCollectionDefaultsToEmpty
),
MacroCache
.
transformerOptionsMapping
.
getOrElseUpdate
(
_
,
mutable
.
Set
.
empty
)
.
remove
(
Options
.
disableCollectionDefaultsToEmpty
)
)
def
disableOptionDefaultsToNoneImpl
[
From
,
To
]
=
setOptions
[
From
,
To
](
MacroCache
.
transformerOptionsMapping
.
getOrElseUpdate
(
_
,
mutable
.
Set
.
empty
)
.
add
(
Options
.
disableOptionDefaultsToNone
),
MacroCache
.
transformerOptionsMapping
.
getOrElseUpdate
(
_
,
mutable
.
Set
.
empty
)
.
remove
(
Options
.
enableCollectionDefaultsToEmpty
)
)
def
disableCollectionDefaultsToEmptyImpl
[
From
,
To
]
=
setOptions
[
From
,
To
](
MacroCache
.
transformerOptionsMapping
.
getOrElseUpdate
(
_
,
mutable
.
Set
.
empty
)
.
add
(
Options
.
disableCollectionDefaultsToEmpty
),
MacroCache
.
transformerOptionsMapping
.
getOrElseUpdate
(
_
,
mutable
.
Set
.
empty
)
.
remove
(
Options
.
enableCollectionDefaultsToEmpty
)
)
private
def
setOptions
[
From
,
To
](
enable
:
Int
=>
Unit
,
disable
:
Int
=>
Unit
)
:
Expr
[
Transformable
[
From
,
To
]]
=
{
val
builderId
=
getBuilderId
(
annoBuilderPrefix
)
enable
(
builderId
)
disable
(
builderId
)
val
tree
=
q
"new ${c.prefix.actualType}"
exprPrintTree
[
Transformable
[
From
,
To
]](
force
=
false
,
tree
)
}
...
...
@@ -140,35 +186,13 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
private
def
getTransformBody
[
From:
WeakTypeTag
,
To:
WeakTypeTag
]
:
Tree
=
{
val
toClassName
=
resolveClassTypeName
[
To
]
val
fromClassName
=
resolveClassTypeName
[
From
]
val
toClassInfo
=
getCaseClassFieldInfo
[
To
]()
val
fromClassInfo
=
getCaseClassFieldInfo
[
From
]()
val
customDefaultValueMapping
=
MacroCache
.
classFieldDefaultValueMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
val
toClassInfo
=
getCaseClassFieldInfoList
[
To
]()
val
fromClassInfo
=
getCaseClassFieldInfoList
[
From
]()
val
customFieldNameMapping
=
MacroCache
.
classFieldNameMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
val
customFieldTypeMapping
=
MacroCache
.
classFieldTypeMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
c
.
info
(
c
.
enclosingPosition
,
s
"Field default value mapping: $customDefaultValueMapping"
,
force
=
true
)
c
.
info
(
c
.
enclosingPosition
,
s
"Field name mapping: $customFieldNameMapping"
,
force
=
true
)
c
.
info
(
c
.
enclosingPosition
,
s
"Field type mapping: $customFieldTypeMapping"
,
force
=
true
)
val
missingFields
=
toClassInfo
.
map
(
_
.
fieldName
).
filterNot
(
fromClassInfo
.
map
(
_
.
fieldName
).
contains
)
val
missingExcludeMappingName
=
missingFields
.
filterNot
(
customFieldNameMapping
.
contains
)
if
(
missingExcludeMappingName
.
nonEmpty
)
{
val
noDefaultValueFields
=
missingExcludeMappingName
.
filterNot
(
customDefaultValueMapping
.
keySet
.
contains
)
if
(
noDefaultValueFields
.
nonEmpty
)
{
c
.
abort
(
c
.
enclosingPosition
,
s
"From type: `$fromClassName` has fewer fields than To type: `$toClassName` and cannot be transformed!"
+
s
"\nMissing field mapping: `$fromClassName`.? => `$toClassName`.`${missingExcludeMappingName.mkString("
,
")}`."
+
s
"\nPlease consider using `setName` or `setDefaultValue` method for `$toClassName`.${missingExcludeMappingName
.mkString("
,
")}!"
)
}
}
val
fields
=
toClassInfo
.
map
{
field
=>
val
fromFieldName
=
customFieldNameMapping
.
get
(
field
.
fieldName
)
val
realToFieldName
=
fromFieldName
.
fold
(
field
.
fieldName
)(
x
=>
x
)
...
...
@@ -179,14 +203,14 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
case
None
if
customFieldTypeMapping
.
contains
(
field
.
fieldName
)
=>
q
"""${TermName(field.fieldName)} = ${TermName(builderFunctionPrefix + field.fieldName)}.apply(${q"$fromTermName.${TermName(realToFieldName)}"})"""
case
_
=>
checkF
ieldGetFieldTerm
[
From
](
checkF
orNoMappingField
[
From
,
To
](
realToFieldName
,
fromClassInfo
.
find
(
_
.
fieldName
==
realToFieldName
),
field
,
custom
DefaultValu
eMapping
custom
FieldNam
eMapping
)
}
}
}
.
filterNot
(
_
==
EmptyTree
)
q
"""
${toClassName.toTermName}.apply(
..$fields
...
...
@@ -194,37 +218,57 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
"""
}
private
def
checkF
ieldGetFieldTerm
[
From
:
WeakTypeTag
](
private
def
checkF
orNoMappingField
[
From:
WeakTypeTag
,
To
:
WeakTypeTag
](
realFromFieldName
:
String
,
fromFieldOpt
:
Option
[
FieldInformation
],
toField
:
FieldInformation
,
custom
DefaultValueMapping
:
mutable.Map
[
String
,
Any
]
custom
FieldNameMapping
:
mutable.Map
[
String
,
String
]
)
:
Tree
=
{
val
toClassInfo
=
getCaseClassFieldInfoList
[
To
]()
val
fromClassInfo
=
getCaseClassFieldInfoList
[
From
]()
val
customOptionsMapping
=
MacroCache
.
transformerOptionsMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Set
.
empty
)
val
customDefaultValueMapping
=
MacroCache
.
classFieldDefaultValueMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
val
fromFieldTerm
=
q
"$fromTermName.${TermName(realFromFieldName)}"
val
fromClassName
=
resolveClassTypeName
[
From
]
fromFieldOpt
match
{
case
Some
(
fromField
)
if
!(
fromField
.
fieldType
weak_<:<
toField
.
fieldType
)
=>
tryFor
Wrap
Type
(
fromFieldTerm
,
fromField
,
toField
)
tryFor
Collection
Type
(
fromFieldTerm
,
fromField
,
toField
)
case
Some
(
fromField
)
if
fromField
.
fieldType
weak_<:<
toField
.
fieldType
=>
q
"${TermName(toField.fieldName)} = $fromFieldTerm"
case
None
if
!
customDefaultValueMapping
.
keySet
.
contains
(
toField
.
fieldName
)
=>
c
.
abort
(
c
.
enclosingPosition
,
s
"The value `$realFromFieldName` is not a member of `$fromClassName`!"
+
s
"\nPlease consider using `setDefaultValue` method!"
)
case
_
=>
case
None
if
customDefaultValueMapping
.
keySet
.
contains
(
toField
.
fieldName
)
=>
val
value
=
q
"""${TermName(builderDefaultValuePrefix$ + toField.fieldName)}"""
q
"${TermName(toField.fieldName)} = $value"
case
_
=>
val
isStrictCollection
=
toField
.
collectionFlags
.
isStrictCollection
&&
customOptionsMapping
.
contains
(
Options
.
enableCollectionDefaultsToEmpty
)
val
isOption
=
toField
.
collectionFlags
.
isOption
&&
customOptionsMapping
.
contains
(
Options
.
enableOptionDefaultsToNone
)
if
(
isStrictCollection
||
isOption
)
{
q
"${TermName(toField.fieldName)} = ${getZeroValue(toField.fieldType)}"
}
else
{
if
(!
toField
.
hasDefaultValue
)
{
checkMissingFields
[
From
,
To
](
fromClassInfo
,
toClassInfo
,
customDefaultValueMapping
,
customFieldNameMapping
,
customOptionsMapping
)
EmptyTree
}
else
{
EmptyTree
}
}
}
}
private
def
tryFor
Wrap
Type
(
fromFieldTerm
:
Tree
,
fromField
:
FieldInformation
,
toField
:
FieldInformation
)
:
Tree
=
private
def
tryFor
Collection
Type
(
fromFieldTerm
:
Tree
,
fromField
:
FieldInformation
,
toField
:
FieldInformation
)
:
Tree
=
(
fromField
,
toField
)
match
{
case
(
FieldInformation
(
_
,
fromFieldType
,
collectionsFlags1
,
genericType1
),
FieldInformation
(
_
,
toFieldType
,
collectionsFlags2
,
genericType2
)
FieldInformation
(
_
,
fromFieldType
,
collectionsFlags1
,
genericType1
,
_
,
_
),
FieldInformation
(
_
,
toFieldType
,
collectionsFlags2
,
genericType2
,
_
,
_
)
)
if
((
collectionsFlags1
.
isSeq
&&
collectionsFlags2
.
isSeq
)
||
(
collectionsFlags1
.
isList
&&
collectionsFlags2
.
isList
)
||
...
...
@@ -246,4 +290,36 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
q
"""${TermName(toField.fieldName)} = $packageName.Transformer[${information1.fieldType}, ${information2.fieldType}].transform($fromFieldTerm)"""
}
private
def
checkMissingFields
[
From:
WeakTypeTag
,
To:
WeakTypeTag
](
fromClassInfo
:
List
[
FieldInformation
],
toClassInfo
:
List
[
FieldInformation
],
customDefaultValueMapping
:
mutable.Map
[
String
,
Any
],
customFieldNameMapping
:
mutable.Map
[
String
,
String
],
customOptionsMapping
:
mutable.Set
[
Options
]
)
=
{
val
toClassName
=
resolveClassTypeName
[
To
]
val
fromClassName
=
resolveClassTypeName
[
From
]
val
missingFields
=
toClassInfo
.
filterNot
(
t
=>
fromClassInfo
.
map
(
_
.
fieldName
).
contains
(
t
.
fieldName
))
val
missingExcludeMappingName
=
missingFields
.
filterNot
(
m
=>
customFieldNameMapping
.
contains
(
m
.
fieldName
))
if
(
missingExcludeMappingName
.
nonEmpty
)
{
val
noDefaultValueFields
=
missingExcludeMappingName
.
filterNot
(
m
=>
customDefaultValueMapping
.
keySet
.
contains
(
m
.
fieldName
))
.
filterNot
(
_
.
hasDefaultValue
)
if
(
noDefaultValueFields
.
nonEmpty
)
{
// scalafmt: { maxColumn = 400 }
val
needHandleFields
=
(
if
(
customOptionsMapping
.
contains
(
Options
.
enableOptionDefaultsToNone
))
{
noDefaultValueFields
.
filterNot
(
_
.
collectionFlags
.
isOption
)
}
else
if
(
customOptionsMapping
.
contains
(
Options
.
enableCollectionDefaultsToEmpty
))
{
noDefaultValueFields
.
filterNot
(
_
.
collectionFlags
.
isStrictCollection
)
}
else
{
noDefaultValueFields
}).
map
(
_
.
fieldName
)
c
.
abort
(
c
.
enclosingPosition
,
s
"Missing field mapping: `$fromClassName`.? => `$toClassName`.[${needHandleFields.mkString("
,
")}]."
+
s
"\nPlease consider using `setName`、`setDefaultValue` or `enable*` methods for `$toClassName`.[${needHandleFields.mkString("
,
")}]!"
)
}
}
}
}
smt-common/src/test/scala/org/bitlap/common/TransformableTest.scala
浏览文件 @
9179156e
...
...
@@ -384,4 +384,128 @@ class TransformableTest extends AnyFlatSpec with Matchers {
| .instance
|"""
.
stripMargin
shouldNot
compile
}
"TransformableTest enable* method"
should
"ok"
in
{
case
class
A1
(
a
:
String
,
b
:
Int
,
cc
:
Long
)
case
class
A2
(
a
:
String
,
b
:
Int
,
c
:
Int
,
d
:
Option
[
String
],
e
:
List
[
String
],
f
:
Seq
[
String
],
g
:
Set
[
String
],
h
:
Vector
[
String
]
)
val
a
=
A1
(
"hello"
,
1
,
2
)
implicit
val
b
:
Transformer
[
A1
,
A2
]
=
Transformable
[
A1
,
A2
]
.
setName
(
_
.
cc
,
_
.
c
)
.
setType
[
Long
,
Int
](
_
.
cc
,
fromField
=>
fromField
.
toInt
)
.
enableOptionDefaultsToNone
.
enableCollectionDefaultsToEmpty
.
instance
a
.
transform
[
A2
](
b
).
toString
shouldEqual
"A2(hello,1,2,None,List(),List(),Set(),Vector())"
implicit
val
b2
:
Transformer
[
A1
,
A2
]
=
Transformable
[
A1
,
A2
]
.
setName
(
_
.
cc
,
_
.
c
)
.
setType
[
Long
,
Int
](
_
.
cc
,
fromField
=>
fromField
.
toInt
)
.
setDefaultValue
[
Vector
[
String
]](
_
.
h
,
Vector
(
"Hello world"
)
)
// Higher priority than enableCollectionDefaultsToEmpty
.
enableCollectionDefaultsToEmpty
.
enableOptionDefaultsToNone
.
instance
a
.
transform
[
A2
](
b2
).
toString
shouldEqual
"A2(hello,1,2,None,List(),List(),Set(),Vector(Hello world))"
implicit
val
b3
:
Transformer
[
A1
,
A2
]
=
Transformable
[
A1
,
A2
]
.
setName
(
_
.
cc
,
_
.
c
)
.
setType
[
Long
,
Int
](
_
.
cc
,
fromField
=>
fromField
.
toInt
)
.
setDefaultValue
[
Option
[
String
]](
_
.
d
,
Option
(
"Hello world"
))
// Higher priority than enableOptionDefaultsToNone
.
enableCollectionDefaultsToEmpty
.
instance
a
.
transform
[
A2
](
b3
).
toString
shouldEqual
"A2(hello,1,2,Some(Hello world),List(),List(),Set(),Vector())"
implicit
val
b4
:
Transformer
[
A1
,
A2
]
=
Transformable
[
A1
,
A2
]
.
setName
(
_
.
cc
,
_
.
c
)
.
setType
[
Long
,
Int
](
_
.
cc
,
fromField
=>
fromField
.
toInt
)
.
setDefaultValue
[
Vector
[
String
]](
_
.
h
,
Vector
(
"Hello world1"
)
)
// Higher priority than enableCollectionDefaultsToEmpty
.
setDefaultValue
[
Option
[
String
]](
_
.
d
,
Option
(
"Hello world2"
))
// Higher priority than enableOptionDefaultsToNone
.
enableOptionDefaultsToNone
.
enableCollectionDefaultsToEmpty
.
instance
a
.
transform
[
A2
](
b4
).
toString
shouldEqual
"A2(hello,1,2,Some(Hello world2),List(),List(),Set(),Vector(Hello world1))"
}
"TransformableTest disable* method"
should
"ok"
in
{
case
class
A1
(
d
:
Option
[
String
])
case
class
A2
(
d
:
Option
[
String
],
e
:
Option
[
String
]
=
Some
(
"option"
),
f
:
Option
[
String
]
=
None
,
h
:
List
[
String
]
=
List
(
"list"
),
i
:
List
[
String
]
=
List
.
empty
)
val
a
=
A1
(
Some
(
"hello a"
))
implicit
val
b1
:
Transformer
[
A1
,
A2
]
=
Transformable
[
A1
,
A2
].
enableCollectionDefaultsToEmpty
.
enableOptionDefaultsToNone
.
instance
a
.
transform
[
A2
](
b1
).
toString
shouldEqual
"A2(Some(hello a),None,None,List(),List())"
implicit
val
b2
:
Transformer
[
A1
,
A2
]
=
Transformable
[
A1
,
A2
]
// This method has a higher priority
.
setDefaultValue
(
_
.
f
,
Option
(
"1"
))
.
disableCollectionDefaultsToEmpty
// use default value, not None
.
disableOptionDefaultsToNone
// use default value, not Empty
.
instance
a
.
transform
[
A2
](
b2
).
toString
shouldEqual
"A2(Some(hello a),Some(option),Some(1),List(list),List())"
}
"TransformableTest disable* is ok"
should
"compile ok"
in
{
case
class
A1
(
d
:
Option
[
String
])
case
class
A2
(
d
:
Option
[
String
],
e
:
Option
[
String
]
=
Some
(
"option"
),
f
:
Option
[
String
]
=
None
,
h
:
List
[
String
]
=
List
(
"list"
),
i
:
List
[
String
]
=
List
.
empty
)
val
a
=
A1
(
Some
(
"hello a"
))
implicit
val
b1
:
Transformer
[
A1
,
A2
]
=
Transformable
[
A1
,
A2
].
instance
a
.
transform
[
A2
](
b1
).
toString
shouldEqual
"A2(Some(hello a),Some(option),None,List(list),List())"
}
"TransformableTest disable* is ok if no default value"
should
"compile failed"
in
{
"""
| case class A1(d: Option[String])
| case class A2(
| d: Option[String],
| e: Option[String],
| f: Option[String] = None,
| h: List[String] = List("list"),
| i: List[String] = List.empty,
| )
|
| val a = A1(Some("hello a"))
| implicit val b1: Transformer[A1, A2] = Transformable[A1, A2].instance
|
| a.transform[A2](b1)
|"""
.
stripMargin
shouldNot
compile
}
}
smt-csv-derive/src/main/scala/org/bitlap/csv/derive/DeriveCsvConverter.scala
浏览文件 @
9179156e
...
...
@@ -21,8 +21,8 @@
package
org.bitlap.csv.derive
import
org.bitlap.common.internal.AbstractMacroProcessor
import
scala.reflect.macros.blackbox
import
org.bitlap.common.AbstractMacroProcessor
import
org.bitlap.csv.
{
Converter
,
CsvFormat
}
/** This is a tool macro for automatic derivation of the base CSV converter.
...
...
smt-csv/src/main/scala/org/bitlap/csv/macros/DeriveCsvableBuilder.scala
浏览文件 @
9179156e
...
...
@@ -21,7 +21,8 @@
package
org.bitlap.csv.macros
import
org.bitlap.common.
{
AbstractMacroProcessor
,
MacroCache
}
import
org.bitlap.common.MacroCache
import
org.bitlap.common.internal.AbstractMacroProcessor
import
org.bitlap.csv.
{
CsvFormat
,
CsvableBuilder
}
import
java.io.File
...
...
smt-csv/src/main/scala/org/bitlap/csv/macros/DeriveScalableBuilder.scala
浏览文件 @
9179156e
...
...
@@ -21,7 +21,8 @@
package
org.bitlap.csv.macros
import
org.bitlap.common.
{
AbstractMacroProcessor
,
MacroCache
}
import
org.bitlap.common.MacroCache
import
org.bitlap.common.internal.AbstractMacroProcessor
import
org.bitlap.csv.
{
CsvFormat
,
ScalableBuilder
}
import
java.io.InputStream
...
...
@@ -160,7 +161,7 @@ class DeriveScalableBuilder(override val c: whitebox.Context) extends AbstractMa
// scalafmt: { maxColumn = 400 }
private
def
scalableBody
[
T:
WeakTypeTag
](
clazzName
:
TypeName
,
innerFuncTermName
:
TermName
)
:
Tree
=
{
val
customTrees
=
MacroCache
.
builderFunctionTrees
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
val
params
=
getCaseClassFieldInfo
[
T
]()
val
params
=
getCaseClassFieldInfo
List
[
T
]()
val
fieldNames
=
params
.
map
(
_
.
fieldName
)
val
fields
=
checkGetFieldTreeInformationList
[
T
](
innerFuncTermName
).
map
{
fieldTreeInformation
=>
val
idx
=
fieldTreeInformation
.
index
...
...
smt-csv/src/main/scala/org/bitlap/csv/macros/DeriveToCaseClass.scala
浏览文件 @
9179156e
...
...
@@ -21,7 +21,7 @@
package
org.bitlap.csv.macros
import
org.bitlap.common.AbstractMacroProcessor
import
org.bitlap.common.
internal.
AbstractMacroProcessor
import
org.bitlap.csv.CsvFormat
import
scala.reflect.macros.blackbox
...
...
smt-csv/src/main/scala/org/bitlap/csv/macros/DeriveToString.scala
浏览文件 @
9179156e
...
...
@@ -21,7 +21,7 @@
package
org.bitlap.csv.macros
import
org.bitlap.common.AbstractMacroProcessor
import
org.bitlap.common.
internal.
AbstractMacroProcessor
import
org.bitlap.csv.CsvFormat
import
scala.reflect.macros.blackbox
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录