FIX: StackOverflowError when using recursive relation within Map

(cherry picked from commit d06bc13b4e1e421546f4997900d5fdc6aa980257)
This commit is contained in:
Jonas Pöhler
2022-04-26 13:53:51 +02:00
committed by Noemi Szemenyei
parent 937b15c07a
commit 4d7a3d135c
2 changed files with 91 additions and 0 deletions
@@ -1,7 +1,10 @@
package io.ebean.bean;
import io.ebean.common.BeanMap;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;
/**
* Helps build toString content taking into account recursion.
@@ -104,6 +107,8 @@ public final class ToStringBuilder {
}
} else if (value instanceof Collection) {
addCollection((Collection<?>) value);
} else if (value instanceof Map) {
addMap(((Map<?, ?>) value));
} else {
String content = String.valueOf(value);
if (content.length() > TRIM_LENGTH) {
@@ -117,6 +122,28 @@ public final class ToStringBuilder {
}
}
public void addMap(Map<?,?> map) {
if (map == null || map.isEmpty()) {
sb.append("{}");
} else {
boolean firstElement = true;
sb.append("{");
for (Map.Entry<?, ?> entry : map.entrySet()) {
if (firstElement) {
firstElement = false;
} else {
sb.append(", ");
}
sb.append(entry.getKey()).append(":");
value(entry.getValue());
if (counter > MAX) {
return;
}
}
sb.append("}");
}
}
/**
* Add a collection of values.
*/
@@ -166,12 +166,76 @@ class ToStringBuilderTest {
assertThat(toStringFor(list)).isEqualTo("{a:Recurse@1(id:1, nm:a), b:Recurse@2(id:2, nm:b)}");
}
@Test
void map_recurse() {
final ParamStore paramStore = new ParamStore(1);
paramStore.map.put("aaa", new ParamValue(1, paramStore));
paramStore.map.put("bbb", new ParamValue(2, paramStore));
assertThat(toStringFor(paramStore))
.isEqualTo("ParamStore@0(id:1, map:{aaa:ParamValue@1(id:1, pm:ParamStore@0), bbb:ParamValue@2(id:2, pm:ParamStore@0)})");
}
private String toStringFor(ToStringAware aware) {
ToStringBuilder builder = new ToStringBuilder();
aware.toString(builder);
return builder.toString();
}
static final class ParamValue implements ToStringAware {
final int id;
final ParamStore parentModel;
ParamValue(final int id, final ParamStore parentModel) {
this.id = id;
this.parentModel = parentModel;
}
public String toString() {
ToStringBuilder builder = new ToStringBuilder();
toString(builder);
return builder.toString();
}
@Override
public void toString(ToStringBuilder builder) {
builder.start(this);
builder.add("id", id);
builder.add("pm", parentModel);
builder.end();
}
}
static final class ParamStore implements ToStringAware {
final int id;
final Map<String, ParamValue> map = new HashMap<>();
ParamStore(final int id) {
this.id = id;
}
public String toString() {
ToStringBuilder builder = new ToStringBuilder();
toString(builder);
return builder.toString();
}
@Override
public void toString(ToStringBuilder builder) {
builder.start(this);
builder.add("id", id);
builder.add("map", map);
builder.end();
}
}
static final class Recurse implements ToStringAware {
final int id;