Skip to content

Commit abf8336

Browse files
committed
Add validation for duplicate and unreachable property paths
1 parent c1f4170 commit abf8336

File tree

10 files changed

+164
-38
lines changed

10 files changed

+164
-38
lines changed

doma-core/src/main/java/org/seasar/doma/message/Message.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,7 @@ public enum Message implements MessageResource {
996996
DOMA4487("An element annotated with @AggregateStrategy must not extend other interfaces."),
997997
DOMA4488(
998998
"To specify propertyPath=\"{0}\", a separate definition of @AssociationLinker(propertyPath=\"{1}\") is required."),
999+
DOMA4489("The property path \"{0}\" is duplicated in another @AssociationLinker."),
9991000

10001001
// other
10011002
DOMA5001(

doma-processor/src/main/java/org/seasar/doma/internal/apt/meta/entity/AggregateStrategyMetaFactory.java

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.seasar.doma.internal.apt.meta.entity;
1717

1818
import java.util.ArrayList;
19+
import java.util.Arrays;
1920
import java.util.Comparator;
2021
import java.util.HashSet;
2122
import java.util.LinkedList;
@@ -81,11 +82,26 @@ public AggregateStrategyMeta createTypeElementMeta(TypeElement typeElement) {
8182
findAssociationLinkerMetas(typeElement, root);
8283
validateAllPropertyPaths(associationLinkerMetas);
8384
validateAllTableAliases(aggregateStrategyAnnot.getTableAliasValue(), associationLinkerMetas);
85+
validateAssociationNavigation(root, associationLinkerMetas);
8486
return new AggregateStrategyMeta(
8587
root, aggregateStrategyAnnot.getTableAliasValue(), associationLinkerMetas);
8688
}
8789

8890
private void validateAllPropertyPaths(List<AssociationLinkerMeta> associationLinkerMetas) {
91+
// validate uniqueness
92+
Set<String> seen = new HashSet<>(associationLinkerMetas.size());
93+
for (AssociationLinkerMeta linkerMeta : associationLinkerMetas) {
94+
if (!seen.add(linkerMeta.propertyPath())) {
95+
throw new AptException(
96+
Message.DOMA4489,
97+
linkerMeta.filedElement(),
98+
linkerMeta.associationLinkerAnnot().getAnnotationMirror(),
99+
linkerMeta.associationLinkerAnnot().getPropertyPath(),
100+
new Object[] {linkerMeta.propertyPath()});
101+
}
102+
}
103+
104+
// validate navigation
89105
Map<String, AssociationLinkerMeta> map =
90106
associationLinkerMetas.stream()
91107
.collect(Collectors.toMap(AssociationLinkerMeta::propertyPath, Function.identity()));
@@ -110,7 +126,38 @@ private void validateAllTableAliases(
110126
for (AssociationLinkerMeta linkerMeta : associationLinkerMetas) {
111127
if (!seen.add(linkerMeta.tableAlias())) {
112128
throw new AptException(
113-
Message.DOMA4481, linkerMeta.filedElement(), new Object[] {linkerMeta.tableAlias()});
129+
Message.DOMA4481,
130+
linkerMeta.filedElement(),
131+
linkerMeta.associationLinkerAnnot().getAnnotationMirror(),
132+
linkerMeta.associationLinkerAnnot().getTableAlias(),
133+
new Object[] {linkerMeta.tableAlias()});
134+
}
135+
}
136+
}
137+
138+
private void validateAssociationNavigation(
139+
EntityCtType root, List<AssociationLinkerMeta> associationLinkerMetas) {
140+
for (AssociationLinkerMeta linkerMeta : associationLinkerMetas) {
141+
TypeMirror source = root.getType();
142+
TypeMirror target = root.getType();
143+
for (String segment : linkerMeta.propertyPathSegments()) {
144+
source = target;
145+
target =
146+
resolveEntity(
147+
source, segment, linkerMeta.filedElement(), linkerMeta.associationLinkerAnnot());
148+
}
149+
BiFunctionMeta biFunctionMeta = linkerMeta.biFunctionMeta();
150+
if (!ctx.getMoreTypes().isSameType(biFunctionMeta.source().getType(), source)) {
151+
throw new AptException(
152+
Message.DOMA4475,
153+
linkerMeta.filedElement(),
154+
new Object[] {biFunctionMeta.source().getType(), source});
155+
}
156+
if (!ctx.getMoreTypes().isSameType(biFunctionMeta.target().getType(), target)) {
157+
throw new AptException(
158+
Message.DOMA4476,
159+
linkerMeta.filedElement(),
160+
new Object[] {biFunctionMeta.target().getType(), target});
114161
}
115162
}
116163
}
@@ -127,7 +174,6 @@ private List<AssociationLinkerMeta> findAssociationLinkerMetas(
127174
}
128175
validateModifiers(fieldElement);
129176
BiFunctionMeta biFunctionMeta = createBiFunctionMeta(fieldElement);
130-
validateAssociation(root, associationLinkerAnnot, biFunctionMeta, fieldElement);
131177
AssociationLinkerMeta associationLinkerMeta =
132178
createAssociationLinkerMeta(
133179
fieldElement, associationLinkerAnnot, biFunctionMeta, aggregateStrategyElement);
@@ -218,31 +264,6 @@ public EntityCtType visitEntityCtType(EntityCtType ctType, String ordinalNumber)
218264
return new BiFunctionMeta(source, target, result);
219265
}
220266

221-
private void validateAssociation(
222-
EntityCtType root,
223-
AssociationLinkerAnnot associationLinkerAnnot,
224-
BiFunctionMeta biFunctionMeta,
225-
VariableElement linkerElement) {
226-
TypeMirror source = root.getType();
227-
TypeMirror target = root.getType();
228-
String[] segments = associationLinkerAnnot.getPropertyPathValue().split("\\.");
229-
if (segments.length == 0) {
230-
throw new AptIllegalStateException("fragments must not be empty.");
231-
}
232-
for (String segment : segments) {
233-
source = target;
234-
target = resolveEntity(source, segment, linkerElement, associationLinkerAnnot);
235-
}
236-
if (!ctx.getMoreTypes().isSameType(biFunctionMeta.source.getType(), source)) {
237-
throw new AptException(
238-
Message.DOMA4475, linkerElement, new Object[] {biFunctionMeta.source.getType(), source});
239-
}
240-
if (!ctx.getMoreTypes().isSameType(biFunctionMeta.target.getType(), target)) {
241-
throw new AptException(
242-
Message.DOMA4476, linkerElement, new Object[] {biFunctionMeta.target.getType(), target});
243-
}
244-
}
245-
246267
private TypeMirror resolveEntity(
247268
TypeMirror typeMirror,
248269
String name,
@@ -303,8 +324,8 @@ private AssociationLinkerMeta createAssociationLinkerMeta(
303324
BiFunctionMeta biFunctionMeta,
304325
TypeElement aggregateStrategyElement) {
305326
String propertyPath = associationLinkerAnnot.getPropertyPathValue();
306-
String[] segments = propertyPath.split("\\.");
307-
int propertyPathDepth = segments.length;
327+
List<String> propertyPathSegments = Arrays.stream(propertyPath.split("\\.")).toList();
328+
int propertyPathDepth = propertyPathSegments.size();
308329
String ancestorPath;
309330
if (propertyPathDepth == 0) {
310331
throw new AptIllegalStateException("propertyPath=" + propertyPath);
@@ -317,13 +338,11 @@ private AssociationLinkerMeta createAssociationLinkerMeta(
317338
associationLinkerAnnot,
318339
ancestorPath,
319340
associationLinkerAnnot.getPropertyPathValue(),
341+
propertyPathSegments,
320342
propertyPathDepth,
321343
associationLinkerAnnot.getTableAliasValue(),
322-
biFunctionMeta.source,
323-
biFunctionMeta.target,
344+
biFunctionMeta,
324345
aggregateStrategyElement,
325346
fieldElement);
326347
}
327-
328-
private record BiFunctionMeta(EntityCtType source, EntityCtType target, EntityCtType result) {}
329348
}

doma-processor/src/main/java/org/seasar/doma/internal/apt/meta/entity/AssociationLinkerMeta.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.seasar.doma.internal.apt.meta.entity;
1717

18+
import java.util.List;
1819
import java.util.Objects;
1920
import javax.lang.model.element.TypeElement;
2021
import javax.lang.model.element.VariableElement;
@@ -25,20 +26,28 @@ public record AssociationLinkerMeta(
2526
AssociationLinkerAnnot associationLinkerAnnot,
2627
String ancestorPath,
2728
String propertyPath,
29+
List<String> propertyPathSegments,
2830
int propertyPathDepth,
2931
String tableAlias,
30-
EntityCtType source,
31-
EntityCtType target,
32+
BiFunctionMeta biFunctionMeta,
3233
TypeElement classElement,
3334
VariableElement filedElement) {
3435

3536
public AssociationLinkerMeta {
3637
Objects.requireNonNull(associationLinkerAnnot);
3738
Objects.requireNonNull(propertyPath);
39+
Objects.requireNonNull(propertyPathSegments);
3840
Objects.requireNonNull(tableAlias);
39-
Objects.requireNonNull(source);
40-
Objects.requireNonNull(target);
41+
Objects.requireNonNull(biFunctionMeta);
4142
Objects.requireNonNull(classElement);
4243
Objects.requireNonNull(filedElement);
4344
}
45+
46+
public EntityCtType source() {
47+
return biFunctionMeta.source();
48+
}
49+
50+
public EntityCtType target() {
51+
return biFunctionMeta.target();
52+
}
4453
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright Doma Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.seasar.doma.internal.apt.meta.entity;
17+
18+
import java.util.Objects;
19+
import org.seasar.doma.internal.apt.cttype.EntityCtType;
20+
21+
public record BiFunctionMeta(EntityCtType source, EntityCtType target, EntityCtType result) {
22+
public BiFunctionMeta {
23+
Objects.requireNonNull(source);
24+
Objects.requireNonNull(target);
25+
Objects.requireNonNull(result);
26+
}
27+
}

doma-processor/src/test/java/org/seasar/doma/internal/apt/processor/aggregate/AggregateStrategyProcessorTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContex
150150
invocationContext(InvalidAssociationSource.class, Message.DOMA4475),
151151
invocationContext(InvalidAssociationTarget.class, Message.DOMA4476),
152152
invocationContext(InvalidPropertyPath.class, Message.DOMA4474),
153-
invocationContext(Subtype.class, Message.DOMA4487));
153+
invocationContext(Subtype.class, Message.DOMA4487),
154+
invocationContext(DuplicatePropertyPath.class, Message.DOMA4489),
155+
invocationContext(UnreachableAssociation.class, Message.DOMA4488));
154156
}
155157

156158
private TestTemplateInvocationContext invocationContext(
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright Doma Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.seasar.doma.internal.apt.processor.aggregate;
17+
18+
import java.util.function.BiFunction;
19+
import org.seasar.doma.AggregateStrategy;
20+
import org.seasar.doma.AssociationLinker;
21+
22+
@AggregateStrategy(root = Emp.class, tableAlias = "e")
23+
interface DuplicatePropertyPath {
24+
25+
@AssociationLinker(propertyPath = "dept", tableAlias = "d")
26+
BiFunction<Emp, Dept, Emp> dept = (e, d) -> e;
27+
28+
@AssociationLinker(propertyPath = "dept", tableAlias = "x")
29+
BiFunction<Emp, Dept, Emp> dept2 = (e, d) -> e;
30+
}

doma-processor/src/test/java/org/seasar/doma/internal/apt/processor/aggregate/InvalidAssociationSource.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121

2222
@AggregateStrategy(root = Dept.class, tableAlias = "d")
2323
interface InvalidAssociationSource {
24+
25+
@AssociationLinker(propertyPath = "employees", tableAlias = "e")
26+
BiFunction<Dept, Emp, Dept> employees = (d, e) -> null;
27+
2428
@AssociationLinker(propertyPath = "employees.address", tableAlias = "a")
2529
BiFunction<Dept, Address, Dept> address = (d, a) -> null;
2630
}

doma-processor/src/test/java/org/seasar/doma/internal/apt/processor/aggregate/InvalidAssociationTarget.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
@AggregateStrategy(root = Dept.class, tableAlias = "d")
2323
interface InvalidAssociationTarget {
24+
@AssociationLinker(propertyPath = "employees", tableAlias = "e")
25+
BiFunction<Dept, Emp, Dept> employees = (d, e) -> null;
26+
2427
@AssociationLinker(propertyPath = "employees.address", tableAlias = "a")
2528
BiFunction<Emp, Dept, Emp> address = (e, d) -> null;
2629
}

doma-processor/src/test/java/org/seasar/doma/internal/apt/processor/aggregate/InvalidPropertyPath.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121

2222
@AggregateStrategy(root = Dept.class, tableAlias = "d")
2323
interface InvalidPropertyPath {
24+
25+
@AssociationLinker(propertyPath = "employees", tableAlias = "e")
26+
BiFunction<Dept, Emp, Dept> employees = (d, e) -> null;
27+
2428
@AssociationLinker(propertyPath = "employees.unknown", tableAlias = "a")
2529
BiFunction<Emp, Address, Emp> address = (e, a) -> null;
2630
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright Doma Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.seasar.doma.internal.apt.processor.aggregate;
17+
18+
import java.util.function.BiFunction;
19+
import org.seasar.doma.AggregateStrategy;
20+
import org.seasar.doma.AssociationLinker;
21+
22+
@AggregateStrategy(root = Dept.class, tableAlias = "d")
23+
interface UnreachableAssociation {
24+
25+
@AssociationLinker(propertyPath = "employeeList.address", tableAlias = "a")
26+
BiFunction<Emp, Address, Emp> address = (e, a) -> e;
27+
}

0 commit comments

Comments
 (0)