Skip to content

Commit edd262a

Browse files
committed
Support initialisation of ToMany collections using List.of() etc
- Add Support for List.of() & Collections.emptyList() - Add Support for Set.of() & Collections.emptySet() - Add Support for Map.of() & Collections.emptyMap()
1 parent cfff738 commit edd262a

File tree

5 files changed

+186
-16
lines changed

5 files changed

+186
-16
lines changed

ebean-agent/src/main/java/io/ebean/enhance/entity/ConstructorDeferredCode.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ enum State {
4545
INVOKE_SPECIAL,
4646
KT_CHECKCAST, // optional kotlin state
4747
KT_LABEL, // optional kotlin state
48-
KT_EMPTYLIST
48+
EMPTY
4949
}
5050

5151
private static final ALoad ALOAD_INSTRUCTION = new ALoad();
@@ -130,11 +130,25 @@ boolean deferVisitMethodInsn(int opcode, String owner, String name, String desc,
130130
stateInitialiseType = owner;
131131
return true;
132132
}
133-
if (opcode == INVOKESTATIC && stateAload() && kotlinEmptyList(owner, name, desc)) {
134-
codes.add(new NoArgInit(opcode, owner, name, desc, itf));
135-
state = State.KT_EMPTYLIST;
136-
stateInitialiseType = "java/util/ArrayList";
137-
return true;
133+
if (opcode == INVOKESTATIC && stateAload()) {
134+
if (emptyList(owner, name, desc) || kotlinEmptyList(owner, name, desc)) {
135+
codes.add(new NoArgInit(opcode, owner, name, desc, itf));
136+
state = State.EMPTY;
137+
stateInitialiseType = "java/util/ArrayList";
138+
return true;
139+
}
140+
if (emptySet(owner, name, desc)) {
141+
codes.add(new NoArgInit(opcode, owner, name, desc, itf));
142+
state = State.EMPTY;
143+
stateInitialiseType = "java/util/LinkedHashSet";
144+
return true;
145+
}
146+
if (emptyMap(owner, name, desc)) {
147+
codes.add(new NoArgInit(opcode, owner, name, desc, itf));
148+
state = State.EMPTY;
149+
stateInitialiseType = "java/util/LinkedHashMap";
150+
return true;
151+
}
138152
}
139153
flush();
140154
return false;
@@ -144,6 +158,24 @@ private boolean isNoArgInit(String name, String desc) {
144158
return name.equals(INIT) && desc.equals(NOARG_VOID);
145159
}
146160

161+
private boolean emptyList(String owner, String name, String desc) {
162+
return desc.equals("()Ljava/util/List;")
163+
&& ((owner.equals("java/util/List") && name.equals("of"))
164+
|| (owner.equals("java/util/Collections") && name.equals("emptyList")));
165+
}
166+
167+
private boolean emptySet(String owner, String name, String desc) {
168+
return desc.equals("()Ljava/util/Set;")
169+
&& ((owner.equals("java/util/Set") && name.equals("of"))
170+
|| (owner.equals("java/util/Collections") && name.equals("emptySet")));
171+
}
172+
173+
private boolean emptyMap(String owner, String name, String desc) {
174+
return desc.equals("()Ljava/util/Map;")
175+
&& ((owner.equals("java/util/Map") && name.equals("of"))
176+
|| (owner.equals("java/util/Collections") && name.equals("emptyMap")));
177+
}
178+
147179
private boolean kotlinEmptyList(String owner, String name, String desc) {
148180
return owner.equals("kotlin/collections/CollectionsKt")
149181
&& name.equals("emptyList")
@@ -232,17 +264,17 @@ private boolean stateInvokeSpecial() {
232264
}
233265

234266
private boolean stateConsumeDeferred() {
235-
return state == State.INVOKE_SPECIAL || state == State.KT_CHECKCAST || state == State.KT_EMPTYLIST;
267+
return state == State.INVOKE_SPECIAL || state == State.KT_CHECKCAST || state == State.EMPTY;
236268
}
237269

238270
/**
239271
* Return true if the type being initialised is valid for auto initialisation of ToMany or DbArray.
240272
*/
241273
private boolean isConsumeManyType() {
242-
return ("java/util/ArrayList".equals(stateInitialiseType)
274+
return "java/util/ArrayList".equals(stateInitialiseType)
243275
|| "java/util/LinkedHashSet".equals(stateInitialiseType)
244-
|| "java/util/HashSet".equals(stateInitialiseType));
245-
//|| "java/util/LinkedHashMap".equals(stateInitialiseType)
276+
|| "java/util/HashSet".equals(stateInitialiseType)
277+
|| "java/util/LinkedHashMap".equals(stateInitialiseType);
246278
//|| "java/util/HashMap".equals(stateInitialiseType));
247279
}
248280

test/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
<properties>
1616
<ebean.version>13.4.0</ebean.version>
17+
<java.version>11</java.version>
1718
</properties>
1819

1920
<dependencies>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package test.enhancement;
2+
3+
import io.ebean.common.BeanList;
4+
import io.ebean.common.BeanMap;
5+
import io.ebean.common.BeanSet;
6+
import org.junit.jupiter.api.Test;
7+
import test.model.Contact;
8+
import test.model.WithInitialisedCollections2;
9+
10+
import static org.assertj.core.api.Assertions.assertThat;
11+
12+
class WithInitialisedCollections2Test extends BaseTest {
13+
14+
@Test
15+
void oneToMany_initialisationCode_expect_removed() {
16+
WithInitialisedCollections2 bean = new WithInitialisedCollections2();
17+
18+
assertThat(bean.listOf()).isInstanceOf(BeanList.class);
19+
assertThat(bean.listCollEmpty()).isInstanceOf(BeanList.class);
20+
assertThat(bean.setOf()).isInstanceOf(BeanSet.class);
21+
assertThat(bean.setCollEmpty()).isInstanceOf(BeanSet.class);
22+
assertThat(bean.mapOf()).isInstanceOf(BeanMap.class);
23+
assertThat(bean.mapCollEmpty()).isInstanceOf(BeanMap.class);
24+
25+
26+
assertThat(bean.transientList()).isNotInstanceOf(BeanList.class);
27+
assertThat(bean.transientList2()).isNotInstanceOf(BeanList.class);
28+
assertThat(bean.transientSet()).isNotInstanceOf(BeanSet.class);
29+
assertThat(bean.transientSet2()).isNotInstanceOf(BeanSet.class);
30+
assertThat(bean.transientMap()).isNotInstanceOf(BeanMap.class);
31+
assertThat(bean.transientMap2()).isNotInstanceOf(BeanMap.class);
32+
33+
34+
// these methods work because the underlying collection is a BeanCollection
35+
bean.listOf().add(new Contact("junk"));
36+
bean.setOf().add(new Contact("junk"));
37+
bean.listCollEmpty().add(new Contact("junk"));
38+
bean.setCollEmpty().add(new Contact("junk"));
39+
}
40+
}

test/src/test/java/test/enhancement/WithInitialisedCollectionsTest.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@
77
import java.util.ArrayList;
88
import java.util.List;
99

10+
import static org.assertj.core.api.Assertions.assertThat;
1011
import static org.junit.jupiter.api.Assertions.*;
1112

1213
public class WithInitialisedCollectionsTest extends BaseTest {
1314

14-
1515
@Test
1616
public void test() {
1717

1818
WithInitialisedCollections bean = new WithInitialisedCollections();
1919
assertNotNull(bean);
2020

21-
assertTrue(bean instanceof EntityBean);
21+
assertInstanceOf(EntityBean.class, bean);
2222

2323
EntityBean eb = (EntityBean)bean;
2424
String[] props = eb._ebean_getPropertyNames();
@@ -43,7 +43,6 @@ public void test() {
4343

4444
assertNotNull(bean.getMyset());
4545
assertNotNull(bean.getMyLinkedSet());
46-
4746
}
4847

4948

@@ -53,7 +52,7 @@ public void test_withTransient() {
5352
WithInitialisedCollectionAndTransient bean = new WithInitialisedCollectionAndTransient();
5453
assertNotNull(bean);
5554

56-
assertTrue(bean instanceof EntityBean);
55+
assertInstanceOf(EntityBean.class, bean);
5756

5857
assertNotNull(bean.getBuffer());
5958

@@ -69,7 +68,7 @@ public void test_withAtTransient() {
6968
WithInitialisedCollectionAndAtTransient bean = new WithInitialisedCollectionAndAtTransient();
7069
assertNotNull(bean);
7170

72-
assertTrue(bean instanceof EntityBean);
71+
assertInstanceOf(EntityBean.class, bean);
7372

7473
assertNotNull(bean.getBuffer());
7574

@@ -87,6 +86,6 @@ public void test_withConstructor() {
8786

8887
WithInitialisedCollectionsAndConstructor bean = new WithInitialisedCollectionsAndConstructor(contacts);
8988

90-
assertTrue(bean.getContacts().size() == 1);
89+
assertThat(bean.getContacts()).hasSize(1);
9190
}
9291
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package test.model;
2+
3+
4+
import javax.persistence.CascadeType;
5+
import javax.persistence.Entity;
6+
import javax.persistence.OneToMany;
7+
import javax.persistence.Transient;
8+
import java.util.*;
9+
10+
@Entity
11+
public class WithInitialisedCollections2 extends BaseEntity {
12+
13+
String name;
14+
15+
@OneToMany(cascade = CascadeType.PERSIST)
16+
final List<Contact> listOf = List.of();
17+
@OneToMany(cascade = CascadeType.PERSIST)
18+
final Set<Contact> setOf = Set.of();
19+
@OneToMany(cascade = CascadeType.PERSIST)
20+
Map<Long,Contact> mapOf = Map.of();
21+
22+
@OneToMany(cascade = CascadeType.PERSIST)
23+
final List<Contact> listCollEmpty = Collections.emptyList();
24+
@OneToMany(cascade = CascadeType.PERSIST)
25+
final Set<Contact> setCollEmpty = Collections.emptySet();
26+
@OneToMany(cascade = CascadeType.PERSIST)
27+
Map<Long,Contact> mapCollEmpty = Collections.emptyMap();
28+
29+
@Transient
30+
List<String> transientList = List.of();
31+
@Transient
32+
Set<String> transientSet = Set.of();
33+
@Transient
34+
Map<String,Contact> transientMap = Map.of();
35+
@Transient
36+
List<String> transientList2 = Collections.emptyList();
37+
@Transient
38+
Set<String> transientSet2 = Collections.emptySet();
39+
@Transient
40+
Map<String,Contact> transientMap2 = Collections.emptyMap();
41+
42+
public String name() {
43+
return name;
44+
}
45+
46+
public WithInitialisedCollections2 setName(String name) {
47+
this.name = name;
48+
return this;
49+
}
50+
51+
public List<Contact> listOf() {
52+
return listOf;
53+
}
54+
55+
public Set<Contact> setOf() {
56+
return setOf;
57+
}
58+
59+
public Map<Long, Contact> mapOf() {
60+
return mapOf;
61+
}
62+
63+
public List<Contact> listCollEmpty() {
64+
return listCollEmpty;
65+
}
66+
67+
public Set<Contact> setCollEmpty() {
68+
return setCollEmpty;
69+
}
70+
71+
public Map<Long, Contact> mapCollEmpty() {
72+
return mapCollEmpty;
73+
}
74+
75+
public List<String> transientList() {
76+
return transientList;
77+
}
78+
79+
public Set<String> transientSet() {
80+
return transientSet;
81+
}
82+
83+
public List<String> transientList2() {
84+
return transientList2;
85+
}
86+
87+
public Set<String> transientSet2() {
88+
return transientSet2;
89+
}
90+
91+
public Map<String, Contact> transientMap() {
92+
return transientMap;
93+
}
94+
95+
public Map<String, Contact> transientMap2() {
96+
return transientMap2;
97+
}
98+
}

0 commit comments

Comments
 (0)