diff --git a/jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/kernel/display/common/Text.java b/jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/kernel/display/common/Text.java
index 3403739..281931c 100644
--- a/jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/kernel/display/common/Text.java
+++ b/jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/kernel/display/common/Text.java
@@ -22,13 +22,17 @@ public static void registerAll(Renderer renderer) {
}
public static void renderCharSequence(CharSequence data, RenderContext context) {
- context.renderIfRequested(JS, () -> data);
- context.renderIfRequested(PLAIN, () -> data);
- context.renderIfRequested(MARKDOWN, () -> data);
- context.renderIfRequested(LATEX, () -> data);
- context.renderIfRequested(HTML, () -> data);
- context.renderIfRequested(CSS, () -> data);
- context.renderIfRequested(SVG, () -> data);
- context.renderIfRequested(JSON, () -> data);
+ // materialize as String: the stored value is later serialized by Gson, which
+ // handles Strings but turns other CharSequence implementations into a reflective
+ // field dump (or fails on JDK-internal types such as CharBuffer)
+ String text = String.valueOf(data);
+ context.renderIfRequested(JS, () -> text);
+ context.renderIfRequested(PLAIN, () -> text);
+ context.renderIfRequested(MARKDOWN, () -> text);
+ context.renderIfRequested(LATEX, () -> text);
+ context.renderIfRequested(HTML, () -> text);
+ context.renderIfRequested(CSS, () -> text);
+ context.renderIfRequested(SVG, () -> text);
+ context.renderIfRequested(JSON, () -> text);
}
}
diff --git a/jjava-jupyter/src/test/java/org/dflib/jjava/jupyter/kernel/display/common/TextTest.java b/jjava-jupyter/src/test/java/org/dflib/jjava/jupyter/kernel/display/common/TextTest.java
new file mode 100644
index 0000000..b2e391d
--- /dev/null
+++ b/jjava-jupyter/src/test/java/org/dflib/jjava/jupyter/kernel/display/common/TextTest.java
@@ -0,0 +1,92 @@
+package org.dflib.jjava.jupyter.kernel.display.common;
+
+import org.dflib.jjava.jupyter.kernel.display.DisplayData;
+import org.dflib.jjava.jupyter.kernel.display.Renderer;
+import org.dflib.jjava.jupyter.kernel.display.mime.MIMEType;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.nio.CharBuffer;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TextTest {
+
+ private Renderer renderer;
+
+ @BeforeEach
+ public void setUp() {
+ this.renderer = new Renderer();
+ Text.registerAll(this.renderer);
+ }
+
+ @Test
+ public void rendersStringAsIs() {
+ DisplayData data = this.renderer.render("hello");
+
+ assertEquals("hello", data.getData(MIMEType.TEXT_PLAIN));
+ }
+
+ @Test
+ public void materializesStringBuilder() {
+ DisplayData data = this.renderer.render(new StringBuilder("hello"));
+
+ assertEquals("hello", data.getData(MIMEType.TEXT_PLAIN));
+ }
+
+ @Test
+ public void materializesCharBuffer() {
+ // CharBuffer is a CharSequence whose implementation classes Gson cannot
+ // serialize reflectively on modern JDKs (JsonIOException)
+ DisplayData data = this.renderer.render(CharBuffer.wrap("hello"));
+
+ assertEquals("hello", data.getData(MIMEType.TEXT_PLAIN));
+ }
+
+ @Test
+ public void materializesCustomCharSequence() {
+ DisplayData data = this.renderer.render(new CustomText("hello"));
+
+ assertEquals("hello", data.getData(MIMEType.TEXT_PLAIN));
+ }
+
+ @Test
+ public void materializesForAlternateMimeTypes() {
+ DisplayData data = this.renderer.renderAs(new CustomText("hi"), MIMEType.TEXT_HTML.toString());
+
+ assertEquals("hi", data.getData(MIMEType.TEXT_HTML));
+ }
+
+ /**
+ * Stand-in for library-provided CharSequence implementations (e.g.
+ * commons-text TextStringBuilder, Groovy GString) that Gson would
+ * otherwise serialize as a reflective field dump.
+ */
+ private static class CustomText implements CharSequence {
+ private final String value;
+
+ CustomText(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public int length() {
+ return value.length();
+ }
+
+ @Override
+ public char charAt(int index) {
+ return value.charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ return value.subSequence(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+ }
+}