-
Notifications
You must be signed in to change notification settings - Fork 43
/
Copy pathParsingStrategy.kt
132 lines (123 loc) · 5.44 KB
/
ParsingStrategy.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package com.sipios.springsearch.strategies
import com.sipios.springsearch.SearchOperation
import com.sipios.springsearch.anotation.SearchSpec
import jakarta.persistence.criteria.CriteriaBuilder
import jakarta.persistence.criteria.Path
import jakarta.persistence.criteria.Predicate
import java.time.Duration
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.util.Date
import java.util.UUID
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
import org.springframework.http.HttpStatus
import org.springframework.web.server.ResponseStatusException
interface ParsingStrategy {
/**
* Method to parse the value specified to the corresponding strategy
*
* @param value Value used for the search
* @param fieldClass Kotlin class of the referred field
* @return Returns by default the value without any parsing
*/
fun parse(value: String?, fieldClass: KClass<out Any>): Any? {
return value
}
fun parse(value: List<*>?, fieldClass: KClass<out Any>): Any? {
return value?.map { parse(it.toString(), fieldClass) }
}
/**
* Method to build the predicate
*
* @param builder Criteria object to build on
* @param path Current path for predicate
* @param fieldName Name of the field to be searched
* @param ops Search operation to use
* @param value Value used for the search
* @return Returns a Predicate instance or null if the operation was not found
*/
fun buildPredicate(
builder: CriteriaBuilder,
path: Path<*>,
fieldName: String,
ops: SearchOperation?,
value: Any?
): Predicate? {
return when (ops) {
SearchOperation.IN_ARRAY -> {
val inClause: CriteriaBuilder.In<Any> = getInClause(builder, path, fieldName, value)
inClause
}
SearchOperation.NOT_IN_ARRAY -> {
val inClause: CriteriaBuilder.In<Any> = getInClause(builder, path, fieldName, value)
builder.not(inClause)
}
SearchOperation.IS -> {
if (value == SearchOperation.NULL) {
builder.isNull(path.get<Any>(fieldName))
} else {
// we should not call parent method for collection fields
// so this makes no sense to search for EMPTY with a non-collection field
throw ResponseStatusException(HttpStatus.BAD_REQUEST,
"Unsupported operation $ops $value for field $fieldName")
}
}
SearchOperation.IS_NOT -> {
if (value == SearchOperation.NULL) {
builder.isNotNull(path.get<Any>(fieldName))
} else {
// we should not call parent method for collection fields
// so this makes no sense to search for NOT EMPTY with a non-collection field
throw ResponseStatusException(HttpStatus.BAD_REQUEST,
"Unsupported operation $ops $value for field $fieldName")
}
}
SearchOperation.EQUALS -> builder.equal(path.get<Any>(fieldName), value)
SearchOperation.NOT_EQUALS -> builder.notEqual(path.get<Any>(fieldName), value)
SearchOperation.STARTS_WITH -> builder.like(path[fieldName], "$value%")
SearchOperation.ENDS_WITH -> builder.like(path[fieldName], "%$value")
SearchOperation.CONTAINS -> builder.like((path.get<String>(fieldName).`as`(String::class.java)), "%$value%")
SearchOperation.DOESNT_START_WITH -> builder.notLike(path[fieldName], "$value%")
SearchOperation.DOESNT_END_WITH -> builder.notLike(path[fieldName], "%$value")
SearchOperation.DOESNT_CONTAIN -> builder.notLike(
(path.get<String>(fieldName).`as`(String::class.java)),
"%$value%"
)
else -> null
}
}
fun getInClause(
builder: CriteriaBuilder,
path: Path<*>,
fieldName: String,
value: Any?
): CriteriaBuilder.In<Any> {
val inClause: CriteriaBuilder.In<Any> = builder.`in`(path.get(fieldName))
val values = value as List<*>
values.forEach { inClause.value(it) }
return inClause
}
companion object {
fun getStrategy(fieldClass: KClass<out Any>, searchSpecAnnotation: SearchSpec, isCollectionField: Boolean): ParsingStrategy {
return when {
isCollectionField -> CollectionStrategy()
fieldClass == Boolean::class -> BooleanStrategy()
fieldClass == Date::class -> DateStrategy()
fieldClass == Double::class -> DoubleStrategy()
fieldClass == Float::class -> FloatStrategy()
fieldClass == Int::class -> IntStrategy()
fieldClass.isSubclassOf(Enum::class) -> EnumStrategy()
fieldClass == Duration::class -> DurationStrategy()
fieldClass == LocalDate::class -> LocalDateStrategy()
fieldClass == LocalTime::class -> LocalTimeStrategy()
fieldClass == LocalDateTime::class -> LocalDateTimeStrategy()
fieldClass == Instant::class -> InstantStrategy()
fieldClass == UUID::class -> UUIDStrategy()
else -> StringStrategy(searchSpecAnnotation)
}
}
}
}