Skip to content

Commit 4fcf4b3

Browse files
authored
Merge pull request #363 from domaframework/virtual-default
Support Kotlin's default methods
2 parents ac1b89a + 6db03d4 commit 4fcf4b3

File tree

9 files changed

+219
-11
lines changed

9 files changed

+219
-11
lines changed

src/main/java/org/seasar/doma/internal/apt/MoreElements.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
import java.lang.annotation.Annotation;
88
import java.util.Arrays;
99
import java.util.Collections;
10+
import java.util.EnumSet;
1011
import java.util.HashMap;
1112
import java.util.Iterator;
1213
import java.util.LinkedHashMap;
1314
import java.util.List;
1415
import java.util.Map;
1516
import java.util.function.Predicate;
17+
import java.util.stream.Stream;
1618
import javax.annotation.processing.ProcessingEnvironment;
1719
import javax.lang.model.element.AnnotationMirror;
1820
import javax.lang.model.element.AnnotationValue;
@@ -26,13 +28,16 @@
2628
import javax.lang.model.element.TypeParameterElement;
2729
import javax.lang.model.element.VariableElement;
2830
import javax.lang.model.type.DeclaredType;
31+
import javax.lang.model.type.ExecutableType;
2932
import javax.lang.model.type.TypeMirror;
3033
import javax.lang.model.util.ElementFilter;
3134
import javax.lang.model.util.Elements;
3235
import javax.lang.model.util.SimpleElementVisitor8;
3336
import org.seasar.doma.ParameterName;
3437
import org.seasar.doma.internal.apt.def.TypeParametersDef;
3538
import org.seasar.doma.internal.apt.util.ElementKindUtil;
39+
import org.seasar.doma.internal.util.Pair;
40+
import org.seasar.doma.internal.util.Zip;
3641

3742
public class MoreElements implements Elements {
3843

@@ -173,6 +178,18 @@ public TypeParameterElement visitTypeParameter(TypeParameterElement e, Void aVoi
173178
null);
174179
}
175180

181+
public ExecutableType toExecutableType(Element element) {
182+
assertNotNull(element);
183+
return element.accept(
184+
new SimpleElementVisitor8<ExecutableType, Void>() {
185+
186+
public ExecutableType visitExecutableType(ExecutableType e, Void p) {
187+
return e;
188+
}
189+
},
190+
null);
191+
}
192+
176193
public TypeElement getTypeElementFromBinaryName(String binaryName) {
177194
assertNotNull(binaryName);
178195
String[] parts = binaryName.split("\\$");
@@ -296,4 +313,51 @@ public VariableElement getSingleParameterOfRecordConstructor(TypeElement record)
296313
.findFirst()
297314
.orElse(null);
298315
}
316+
317+
public boolean isVirtualDefaultMethod(TypeElement typeElement, ExecutableElement methodElement) {
318+
return ElementFilter.typesIn(typeElement.getEnclosedElements()).stream()
319+
.filter(t -> t.getSimpleName().contentEquals("DefaultImpls"))
320+
.anyMatch(
321+
t ->
322+
ElementFilter.methodsIn(t.getEnclosedElements()).stream()
323+
.filter(
324+
m -> {
325+
EnumSet<Modifier> set = EnumSet.of(Modifier.PUBLIC, Modifier.STATIC);
326+
return m.getModifiers().containsAll(set);
327+
})
328+
.filter(
329+
m -> {
330+
Name name1 = m.getSimpleName();
331+
Name name2 = methodElement.getSimpleName();
332+
return name1.contentEquals(name2);
333+
})
334+
.filter(
335+
m -> {
336+
TypeMirror type1 = m.getReturnType();
337+
TypeMirror type2 = methodElement.getReturnType();
338+
return ctx.getMoreTypes().isSameType(type1, type2);
339+
})
340+
.filter(
341+
m -> {
342+
int size1 = m.getParameters().size();
343+
int size2 = methodElement.getParameters().size() + 1;
344+
return size1 == size2;
345+
})
346+
.filter(
347+
m -> {
348+
TypeMirror type1 = m.getParameters().iterator().next().asType();
349+
TypeMirror type2 = typeElement.asType();
350+
return ctx.getMoreTypes().isSameTypeWithErasure(type1, type2);
351+
})
352+
.anyMatch(
353+
m -> {
354+
Stream<? extends VariableElement> parameters1 =
355+
m.getParameters().stream().skip(1);
356+
Stream<? extends VariableElement> parameters2 =
357+
methodElement.getParameters().stream();
358+
return Zip.stream(parameters1, parameters2)
359+
.map(pair -> new Pair<>(pair.fst.asType(), pair.snd.asType()))
360+
.allMatch(p -> ctx.getMoreTypes().isSameType(p.fst, p.snd));
361+
}));
362+
}
299363
}

src/main/java/org/seasar/doma/internal/apt/generator/DaoImplMethodGenerator.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,11 @@ public Void visitDefaultQueryMeta(DefaultQueryMeta m) {
661661
} else {
662662
iprint("%1$s __result = ", resultMeta.getType());
663663
}
664-
print("%1$s.super.%2$s(", daoMeta.getTypeElement(), m.getName());
664+
if (m.isVirtual()) {
665+
print("%1$s.DefaultImpls.%2$s(this, ", daoMeta.getTypeElement(), m.getName());
666+
} else {
667+
print("%1$s.super.%2$s(", daoMeta.getTypeElement(), m.getName());
668+
}
665669
for (Iterator<QueryParameterMeta> it = m.getParameterMetas().iterator(); it.hasNext(); ) {
666670
QueryParameterMeta parameterMeta = it.next();
667671
print("%1$s", parameterMeta.getName());

src/main/java/org/seasar/doma/internal/apt/meta/dao/DaoMetaFactory.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
import java.util.Optional;
1616
import java.util.Set;
1717
import java.util.function.BiFunction;
18-
import javax.lang.model.element.*;
18+
import javax.lang.model.element.AnnotationMirror;
19+
import javax.lang.model.element.AnnotationValue;
20+
import javax.lang.model.element.ExecutableElement;
21+
import javax.lang.model.element.Modifier;
22+
import javax.lang.model.element.TypeElement;
23+
import javax.lang.model.element.VariableElement;
1924
import javax.lang.model.type.DeclaredType;
2025
import javax.lang.model.type.TypeMirror;
2126
import javax.lang.model.util.ElementFilter;
@@ -255,6 +260,7 @@ private ExecutableElement findNonDefaultMethod(TypeElement interfaceElement) {
255260
Optional<ExecutableElement> method =
256261
ElementFilter.methodsIn(interfaceElement.getEnclosedElements()).stream()
257262
.filter(m -> !m.isDefault())
263+
.filter(m -> !ctx.getMoreElements().isVirtualDefaultMethod(interfaceElement, m))
258264
.findAny();
259265
if (method.isPresent()) {
260266
return method.get();
@@ -276,26 +282,27 @@ private void doMethodElements(TypeElement interfaceElement, DaoMeta daoMeta) {
276282
for (ExecutableElement methodElement :
277283
ElementFilter.methodsIn(interfaceElement.getEnclosedElements())) {
278284
try {
279-
doMethodElement(daoMeta, methodElement);
285+
doMethodElement(interfaceElement, daoMeta, methodElement);
280286
} catch (AptException e) {
281287
ctx.getReporter().report(e);
282288
daoMeta.setError(true);
283289
}
284290
}
285291
}
286292

287-
private void doMethodElement(DaoMeta daoMeta, ExecutableElement methodElement) {
293+
private void doMethodElement(
294+
TypeElement interfaceElement, DaoMeta daoMeta, ExecutableElement methodElement) {
288295
Set<Modifier> modifiers = methodElement.getModifiers();
289296
if (modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.PRIVATE)) {
290297
return;
291298
}
292-
validateMethod(methodElement);
299+
validateMethod(interfaceElement, methodElement);
293300
QueryMeta queryMeta = createQueryMeta(daoMeta, methodElement);
294301
validateQueryMeta(queryMeta, methodElement);
295302
daoMeta.addQueryMeta(queryMeta);
296303
}
297304

298-
private void validateMethod(ExecutableElement methodElement) {
305+
private void validateMethod(TypeElement interfaceElement, ExecutableElement methodElement) {
299306
TypeElement foundAnnotationTypeElement = null;
300307
for (AnnotationMirror annotation : methodElement.getAnnotationMirrors()) {
301308
DeclaredType declaredType = annotation.getAnnotationType();
@@ -309,7 +316,8 @@ private void validateMethod(ExecutableElement methodElement) {
309316
foundAnnotationTypeElement.getQualifiedName(), typeElement.getQualifiedName()
310317
});
311318
}
312-
if (methodElement.isDefault()) {
319+
if (methodElement.isDefault()
320+
|| ctx.getMoreElements().isVirtualDefaultMethod(interfaceElement, methodElement)) {
313321
throw new AptException(
314322
Message.DOMA4252, methodElement, new Object[] {typeElement.getQualifiedName()});
315323
}

src/main/java/org/seasar/doma/internal/apt/meta/query/DefaultQueryMeta.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,20 @@
55

66
public class DefaultQueryMeta extends AbstractQueryMeta {
77

8-
public DefaultQueryMeta(TypeElement daoElement, ExecutableElement methodElement) {
8+
private boolean isVirtual;
9+
10+
public DefaultQueryMeta(
11+
TypeElement daoElement, ExecutableElement methodElement, boolean isVirtual) {
912
super(daoElement, methodElement);
13+
this.isVirtual = isVirtual;
1014
}
1115

1216
@Override
1317
public <R> R accept(QueryMetaVisitor<R> visitor) {
1418
return visitor.visitDefaultQueryMeta(this);
1519
}
20+
21+
public boolean isVirtual() {
22+
return isVirtual;
23+
}
1624
}

src/main/java/org/seasar/doma/internal/apt/meta/query/DefaultQueryMetaFactory.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ public DefaultQueryMetaFactory(
1414

1515
@Override
1616
public QueryMeta createQueryMeta() {
17-
if (!methodElement.isDefault()) {
17+
boolean isVirtualDefaultMethod =
18+
ctx.getMoreElements().isVirtualDefaultMethod(daoElement, methodElement);
19+
if (!isVirtualDefaultMethod && !methodElement.isDefault()) {
1820
return null;
1921
}
20-
DefaultQueryMeta queryMeta = new DefaultQueryMeta(daoElement, methodElement);
22+
DefaultQueryMeta queryMeta =
23+
new DefaultQueryMeta(daoElement, methodElement, isVirtualDefaultMethod);
2124
queryMeta.setQueryKind(QueryKind.DEFAULT);
2225
doTypeParameters(queryMeta);
2326
doParameters(queryMeta);

src/test/java/org/seasar/doma/internal/apt/MoreElementsTest.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package org.seasar.doma.internal.apt;
22

3-
import static org.junit.jupiter.api.Assertions.*;
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
6+
import static org.junit.jupiter.api.Assertions.assertNotNull;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
48

59
import java.util.Arrays;
610
import java.util.Collections;
@@ -48,6 +52,23 @@ private class IntersectionType<T extends Number & Runnable> {}
4852
@SuppressWarnings("unused")
4953
private class ReferredTypeVar<T extends Number, S extends List<T>> {}
5054

55+
private interface MyDao {
56+
57+
void doIt();
58+
59+
Integer execute(String name);
60+
61+
Integer run(String name);
62+
63+
class DefaultImpls {
64+
public static void doIt(MyDao $this) {}
65+
66+
public static Integer execute(MyDao $this, String name) {
67+
return null;
68+
}
69+
}
70+
}
71+
5172
@BeforeEach
5273
void beforeEach() {
5374
addCompilationUnit(getClass());
@@ -250,4 +271,21 @@ protected void run() {
250271
}
251272
});
252273
}
274+
275+
@Test
276+
void isVirtualDefaultMethod() {
277+
addProcessor(
278+
new TestProcessor() {
279+
@Override
280+
protected void run() {
281+
TypeElement typeElement = ctx.getMoreElements().getTypeElement(MyDao.class);
282+
ExecutableElement doIt = createMethodElement(MyDao.class, "doIt");
283+
assertTrue(ctx.getMoreElements().isVirtualDefaultMethod(typeElement, doIt));
284+
ExecutableElement execute = createMethodElement(MyDao.class, "execute", String.class);
285+
assertTrue(ctx.getMoreElements().isVirtualDefaultMethod(typeElement, execute));
286+
ExecutableElement run = createMethodElement(MyDao.class, "run", String.class);
287+
assertFalse(ctx.getMoreElements().isVirtualDefaultMethod(typeElement, run));
288+
}
289+
});
290+
}
253291
}

src/test/java/org/seasar/doma/internal/apt/processor/dao/DaoProcessorTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContex
114114
invocationContext(StaticMethodDao.class),
115115
invocationContext(PackageAccessLevelDao.class),
116116
invocationContext(DefaultMethodDao.class),
117+
invocationContext(VirtualDefaultMethodDao.class),
117118
invocationContext(SingletonConfigDao.class),
118119
invocationContext(OptionalIntDao.class),
119120
invocationContext(OptionalLongDao.class),
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.seasar.doma.internal.apt.processor.dao;
2+
3+
import java.math.BigDecimal;
4+
import org.seasar.doma.Dao;
5+
6+
@Dao(config = MyConfig.class)
7+
public interface VirtualDefaultMethodDao {
8+
9+
BigDecimal execute(String aaa, Integer bbb);
10+
11+
class DefaultImpls {
12+
public static BigDecimal execute(VirtualDefaultMethodDao $this, String aaa, Integer bbb) {
13+
return BigDecimal.ONE;
14+
}
15+
}
16+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package org.seasar.doma.internal.apt.processor.dao;
2+
3+
/** */
4+
@javax.annotation.Generated(value = { "Doma", "@VERSION@" }, date = "1970-01-01T09:00:00.000+0900")
5+
public class VirtualDefaultMethodDaoImpl extends org.seasar.doma.internal.jdbc.dao.AbstractDao implements org.seasar.doma.internal.apt.processor.dao.VirtualDefaultMethodDao {
6+
7+
static {
8+
org.seasar.doma.internal.Artifact.validateVersion("@VERSION@");
9+
}
10+
11+
/** */
12+
public VirtualDefaultMethodDaoImpl() {
13+
super(new org.seasar.doma.internal.apt.processor.dao.MyConfig());
14+
}
15+
16+
/**
17+
* @param connection the connection
18+
*/
19+
public VirtualDefaultMethodDaoImpl(java.sql.Connection connection) {
20+
super(new org.seasar.doma.internal.apt.processor.dao.MyConfig(), connection);
21+
}
22+
23+
/**
24+
* @param dataSource the dataSource
25+
*/
26+
public VirtualDefaultMethodDaoImpl(javax.sql.DataSource dataSource) {
27+
super(new org.seasar.doma.internal.apt.processor.dao.MyConfig(), dataSource);
28+
}
29+
30+
/**
31+
* @param config the configuration
32+
*/
33+
protected VirtualDefaultMethodDaoImpl(org.seasar.doma.jdbc.Config config) {
34+
super(config);
35+
}
36+
37+
/**
38+
* @param config the configuration
39+
* @param connection the connection
40+
*/
41+
protected VirtualDefaultMethodDaoImpl(org.seasar.doma.jdbc.Config config, java.sql.Connection connection) {
42+
super(config, connection);
43+
}
44+
45+
/**
46+
* @param config the configuration
47+
* @param dataSource the dataSource
48+
*/
49+
protected VirtualDefaultMethodDaoImpl(org.seasar.doma.jdbc.Config config, javax.sql.DataSource dataSource) {
50+
super(config, dataSource);
51+
}
52+
53+
@Override
54+
public java.math.BigDecimal execute(java.lang.String aaa, java.lang.Integer bbb) {
55+
entering("org.seasar.doma.internal.apt.processor.dao.VirtualDefaultMethodDaoImpl", "execute", aaa, bbb);
56+
try {
57+
java.math.BigDecimal __result = org.seasar.doma.internal.apt.processor.dao.VirtualDefaultMethodDao.DefaultImpls.execute(this, aaa, bbb);
58+
exiting("org.seasar.doma.internal.apt.processor.dao.VirtualDefaultMethodDaoImpl", "execute", __result);
59+
return __result;
60+
} catch (java.lang.RuntimeException __e) {
61+
throwing("org.seasar.doma.internal.apt.processor.dao.VirtualDefaultMethodDaoImpl", "execute", __e);
62+
throw __e;
63+
}
64+
}
65+
66+
}

0 commit comments

Comments
 (0)