Skip to content

Commit 2013143

Browse files
authored
Closes 54 (#56)
* Closes 54
1 parent 2cebb83 commit 2013143

File tree

8 files changed

+308
-15
lines changed

8 files changed

+308
-15
lines changed

.github/badges/branches.svg

Lines changed: 1 addition & 1 deletion
Loading

readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ Apache Maven
2323

2424
Gradle Kotlin DSL
2525
```kotlin
26-
implementation("org.xpathqs:xpathqs-core:0.0.3")
26+
implementation("org.xpathqs:xpathqs-core:0.0.4")
2727
```
2828

2929
Gradle Groovy DSL
3030
```groovy
31-
implementation 'org.xpathqs:xpathqs-core:0.0.3'
31+
implementation 'org.xpathqs:xpathqs-core:0.0.4'
3232
```
3333

3434
## Quick Example
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2021 Nikita A. Chegodaev
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
package org.xpathqs.core.annotations
24+
25+
/**
26+
* Annotation to tell [org.xpathqs.core.reflection.PackageScanner] to
27+
* ignore annotated object/class for scanning and updating via reflection.
28+
* <strong>Important!</strong> [AnnotationTarget.PROPERTY] target should be avoided. If it will
29+
* be present, than Java-Reflection API will not work at all for this annotations
30+
* @sample org.xpathqs.core.reflection.PageWithNoScan
31+
* @sample org.xpathqs.core.reflection.parser.ObjectWithNoScanTest
32+
*/
33+
@Target(
34+
AnnotationTarget.CLASS,
35+
AnnotationTarget.FIELD)
36+
@Retention(AnnotationRetention.RUNTIME)
37+
@MustBeDocumented
38+
annotation class NoScan

src/main/kotlin/org/xpathqs/core/reflection/SelectorReflectionFields.kt

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
package org.xpathqs.core.reflection
2424

25+
import org.xpathqs.core.annotations.NoScan
2526
import org.xpathqs.core.selector.Block
2627
import org.xpathqs.core.selector.base.BaseSelector
2728
import java.lang.reflect.Field
@@ -58,11 +59,13 @@ internal class SelectorReflectionFields(
5859
val res = ArrayList<Field>()
5960

6061
var cls = rootObj::class.java
61-
res.addAll(cls.declaredFields)
62-
63-
while (cls.superclass.isSelectorSubtype()) {
64-
cls = cls.superclass as Class<out BaseSelector>
62+
if(cls.isScanAvailable) {
6563
res.addAll(cls.declaredFields)
64+
65+
while (cls.superclass.isSelectorSubtype() && cls.isScanAvailable) {
66+
cls = cls.superclass as Class<out BaseSelector>
67+
res.addAll(cls.declaredFields)
68+
}
6669
}
6770

6871
removeUnnecessary(res)
@@ -91,22 +94,35 @@ internal class SelectorReflectionFields(
9194
val innerObjectClasses: Collection<Class<*>>
9295
by lazy {
9396
val res = ArrayList<Class<*>>()
94-
95-
if (rootObj::class.java.simpleName != BaseSelector::class.java.simpleName) {
96-
rootObj::class.java.declaredClasses.forEach {
97-
if (it.isSelectorSubtype()) {
98-
res.add(it)
97+
val rootCls = rootObj::class.java
98+
if(rootCls.isScanAvailable) {
99+
if (rootCls.simpleName != BaseSelector::class.java.simpleName) {
100+
rootCls.declaredClasses.forEach {
101+
if (it.isSelectorSubtype()) {
102+
if(it.isScanAvailable) {
103+
res.add(it)
104+
}
105+
}
99106
}
100107
}
101108
}
102-
103109
res
104110
}
105111

106112
/**
107113
* Filter Unnecessary fields
108114
*/
109115
private fun removeUnnecessary(fields: Collection<Field>) = fields
110-
.filter { it.name != "INSTANCE" && it.name != "\$jacocoData" }
116+
.filter {
117+
it.name != "INSTANCE"
118+
&& it.name != "\$jacocoData"
119+
&& it.isScanAvailable
120+
}
111121
.distinctBy { it.name }
122+
123+
private val Field.isScanAvailable: Boolean
124+
get() = !this.isAnnotationPresent(NoScan::class.java)
125+
126+
private val Class<*>.isScanAvailable: Boolean
127+
get() = !this.isAnnotationPresent(NoScan::class.java)
112128
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (c) 2021 Nikita A. Chegodaev
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
package org.xpathqs.core.reflection
24+
25+
import assertk.assertThat
26+
import assertk.assertions.hasSize
27+
import org.junit.jupiter.api.Test
28+
29+
internal class SelectorReflectionFieldsNoScanTest {
30+
31+
@Test
32+
fun noScanForInnerSelectorTopLevel() {
33+
assertThat(
34+
SelectorReflectionFields(PageWithNoScan)
35+
.innerSelectors
36+
).hasSize(3)
37+
}
38+
39+
@Test
40+
fun noScanForInnerObjectTopLevel() {
41+
assertThat(
42+
SelectorReflectionFields(PageWithNoScan)
43+
.innerObjectClasses
44+
).hasSize(2)
45+
}
46+
47+
@Test
48+
fun noScanForMemberSelectorTopLevel() {
49+
assertThat(
50+
SelectorReflectionFields(PageWithNoScan.holder1)
51+
.innerSelectors
52+
).hasSize(2)
53+
}
54+
55+
@Test
56+
fun noScanForMemberObjectSelectorTopLevel() {
57+
assertThat(
58+
SelectorReflectionFields(PageWithNoScan.InnerObject1)
59+
.innerSelectors
60+
).hasSize(1)
61+
}
62+
}

src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsTest.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import assertk.assertThat
2626
import assertk.assertions.containsExactlyInAnyOrder
2727
import org.junit.jupiter.api.Test
2828
import org.xpathqs.core.selector.selector.Selector
29-
import java.lang.reflect.Field
3029

3130
internal class SelectorReflectionFieldsTest {
3231

src/test/kotlin/org/xpathqs/core/reflection/pages.kt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
package org.xpathqs.core.reflection
2424

25+
import org.xpathqs.core.annotations.NoScan
2526
import org.xpathqs.core.selector.Block
2627
import org.xpathqs.core.selector.base.BaseSelector
2728
import org.xpathqs.core.selector.extensions.plus
@@ -106,6 +107,13 @@ open class HolderWithArgs(
106107
val sel2: BaseSelector
107108
): Block(base)
108109

110+
open class HolderWithArgsNoScan(
111+
base: Selector,
112+
val sel1: BaseSelector,
113+
@NoScan
114+
val sel2: BaseSelector
115+
): Block(base)
116+
109117
object PageWithBlockMembers : Block(tagSelector("base")) {
110118
val holder1 = SomeHolder()
111119
val holder2 = SomeHolder()
@@ -137,4 +145,59 @@ object PageWithInnerObjectClassArg: Block() {
137145
tagSelector("s1"),
138146
tagSelector("s2")
139147
)
148+
}
149+
150+
object PageWithNoScan: Block(tagSelector("base")) {
151+
val s1 = tagSelector("s1")
152+
@NoScan val s2 = tagSelector("s2")
153+
154+
object InnerObject1: Block(tagSelector("base2")) {
155+
val s1 = tagSelector("s1")
156+
@NoScan val s2 = tagSelector("s1")
157+
}
158+
159+
@NoScan
160+
object InnerObject2: Block(tagSelector("base2")) {
161+
val s1 = tagSelector("s1")
162+
val s2 = tagSelector("s1")
163+
}
164+
165+
val holder1 = HolderWithArgs(
166+
tagSelector("base"),
167+
tagSelector("s1"),
168+
tagSelector("s2")
169+
)
170+
171+
@NoScan
172+
val holder2 = HolderWithArgs(
173+
tagSelector("base_2"),
174+
tagSelector("s1"),
175+
tagSelector("s2")
176+
)
177+
178+
val holder_ns1 = HolderWithArgsNoScan(
179+
tagSelector("base_2"),
180+
tagSelector("s1"),
181+
tagSelector("s2")
182+
)
183+
184+
@NoScan
185+
val holder_ns2 = HolderWithArgsNoScan(
186+
tagSelector("base_2"),
187+
tagSelector("s1"),
188+
tagSelector("s2")
189+
)
190+
191+
object Holder3: HolderWithArgs(
192+
tagSelector("base"),
193+
tagSelector("s1"),
194+
tagSelector("s2")
195+
)
196+
197+
@NoScan
198+
object Holder4: HolderWithArgs(
199+
tagSelector("base_2"),
200+
tagSelector("s1"),
201+
tagSelector("s2")
202+
)
140203
}

0 commit comments

Comments
 (0)