This is an automated email from the ASF dual-hosted git repository.
nizhikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 58e9d362374 IGNITE-27775 Generate custom array code (#12714)
58e9d362374 is described below
commit 58e9d3623747551b33698d5776217839eac5294c
Author: Nikolay <[email protected]>
AuthorDate: Wed Feb 11 10:30:37 2026 +0300
IGNITE-27775 Generate custom array code (#12714)
---
.../internal/idto/IDTOSerializerGenerator.java | 253 +++++++++++++--------
1 file changed, 154 insertions(+), 99 deletions(-)
diff --git
a/modules/codegen/src/main/java/org/apache/ignite/internal/idto/IDTOSerializerGenerator.java
b/modules/codegen/src/main/java/org/apache/ignite/internal/idto/IDTOSerializerGenerator.java
index 743e159cee5..04957552927 100644
---
a/modules/codegen/src/main/java/org/apache/ignite/internal/idto/IDTOSerializerGenerator.java
+++
b/modules/codegen/src/main/java/org/apache/ignite/internal/idto/IDTOSerializerGenerator.java
@@ -37,7 +37,8 @@ import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
-import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import javax.annotation.processing.FilerException;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
@@ -83,23 +84,23 @@ public class IDTOSerializerGenerator {
/** */
private static final IgniteBiTuple<String, String> OBJECT_SERDES =
- F.t("out.writeObject(obj.${f});", "(${c})in.readObject()");
+ F.t("out.writeObject(${var});", "(${Type})in.readObject()");
/** */
private static final IgniteBiTuple<String, String> STR_STR_MAP =
- F.t("U.writeStringMap(out, obj.${f});", "U.readStringMap(in)");
+ F.t("U.writeStringMap(out, ${var});", "U.readStringMap(in)");
/** Type name to write/read code for the type. */
private static final Map<String, IgniteBiTuple<String, String>>
TYPE_SERDES = new HashMap<>();
{
- TYPE_SERDES.put(boolean.class.getName(),
F.t("out.writeBoolean(obj.${f});", "in.readBoolean()"));
- TYPE_SERDES.put(byte.class.getName(), F.t("out.write(obj.${f});",
"in.read()"));
- TYPE_SERDES.put(short.class.getName(),
F.t("out.writeShort(obj.${f});", "in.readShort()"));
- TYPE_SERDES.put(int.class.getName(), F.t("out.writeInt(obj.${f});",
"in.readInt()"));
- TYPE_SERDES.put(long.class.getName(), F.t("out.writeLong(obj.${f});",
"in.readLong()"));
- TYPE_SERDES.put(float.class.getName(),
F.t("out.writeFloat(obj.${f});", "in.readFloat()"));
- TYPE_SERDES.put(double.class.getName(),
F.t("out.writeDouble(obj.${f});", "in.readDouble()"));
+ TYPE_SERDES.put(boolean.class.getName(),
F.t("out.writeBoolean(${var})", "in.readBoolean()"));
+ TYPE_SERDES.put(byte.class.getName(), F.t("out.write(${var})",
"in.read()"));
+ TYPE_SERDES.put(short.class.getName(), F.t("out.writeShort(${var})",
"in.readShort()"));
+ TYPE_SERDES.put(int.class.getName(), F.t("out.writeInt(${var})",
"in.readInt()"));
+ TYPE_SERDES.put(long.class.getName(), F.t("out.writeLong(${var})",
"in.readLong()"));
+ TYPE_SERDES.put(float.class.getName(), F.t("out.writeFloat(${var})",
"in.readFloat()"));
+ TYPE_SERDES.put(double.class.getName(), F.t("out.writeDouble(${var})",
"in.readDouble()"));
TYPE_SERDES.put(Boolean.class.getName(), OBJECT_SERDES);
TYPE_SERDES.put(Byte.class.getName(), OBJECT_SERDES);
@@ -112,9 +113,9 @@ public class IDTOSerializerGenerator {
TYPE_SERDES.put(Exception.class.getName(), OBJECT_SERDES);
TYPE_SERDES.put(Object.class.getName(), OBJECT_SERDES);
- TYPE_SERDES.put(String.class.getName(), F.t("U.writeString(out,
obj.${f});", "U.readString(in)"));
- TYPE_SERDES.put(UUID.class.getName(), F.t("U.writeUuid(out,
obj.${f});", "U.readUuid(in)"));
- TYPE_SERDES.put("org.apache.ignite.lang.IgniteUuid",
F.t("U.writeIgniteUuid(out, obj.${f});", "U.readIgniteUuid(in)"));
+ TYPE_SERDES.put(String.class.getName(), F.t("U.writeString(out,
${var})", "U.readString(in)"));
+ TYPE_SERDES.put(UUID.class.getName(), F.t("U.writeUuid(out, ${var})",
"U.readUuid(in)"));
+ TYPE_SERDES.put("org.apache.ignite.lang.IgniteUuid",
F.t("U.writeIgniteUuid(out, ${var});", "U.readIgniteUuid(in)"));
TYPE_SERDES.put("org.apache.ignite.internal.processors.cache.version.GridCacheVersion",
OBJECT_SERDES);
TYPE_SERDES.put("org.apache.ignite.lang.IgniteProductVersion",
OBJECT_SERDES);
TYPE_SERDES.put("org.apache.ignite.internal.binary.BinaryMetadata",
OBJECT_SERDES);
@@ -122,34 +123,32 @@ public class IDTOSerializerGenerator {
TYPE_SERDES.put("org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion",
OBJECT_SERDES);
TYPE_SERDES.put("org.apache.ignite.cluster.ClusterNode",
OBJECT_SERDES);
TYPE_SERDES.put("org.apache.ignite.cache.CacheMode",
- F.t("out.writeByte(CacheMode.toCode(obj.${f}));",
"CacheMode.fromCode(in.readByte())"));
-
- TYPE_SERDES.put(TreeMap.class.getName(), F.t("U.writeMap(out,
obj.${f});", "U.readTreeMap(in)"));
- TYPE_SERDES.put(LinkedHashMap.class.getName(), F.t("U.writeMap(out,
obj.${f});", "U.readLinkedMap(in)"));
- TYPE_SERDES.put(Map.class.getName(), F.t("U.writeMap(out, obj.${f});",
"U.readMap(in)"));
- TYPE_SERDES.put(Collection.class.getName(),
F.t("U.writeCollection(out, obj.${f});", "U.readCollection(in)"));
- TYPE_SERDES.put(List.class.getName(), F.t("U.writeCollection(out,
obj.${f});", "U.readList(in)"));
- TYPE_SERDES.put(Set.class.getName(), F.t("U.writeCollection(out,
obj.${f});", "U.readSet(in)"));
+ F.t("out.writeByte(CacheMode.toCode(${var}));",
"CacheMode.fromCode(in.readByte())"));
+
+ TYPE_SERDES.put(TreeMap.class.getName(), F.t("U.writeMap(out,
${var})", "U.readTreeMap(in)"));
+ TYPE_SERDES.put(LinkedHashMap.class.getName(), F.t("U.writeMap(out,
${var})", "U.readLinkedMap(in)"));
+ TYPE_SERDES.put(Map.class.getName(), F.t("U.writeMap(out, ${var})",
"U.readMap(in)"));
+ TYPE_SERDES.put(Collection.class.getName(),
F.t("U.writeCollection(out, ${var})", "U.readCollection(in)"));
+ TYPE_SERDES.put(List.class.getName(), F.t("U.writeCollection(out,
${var})", "U.readList(in)"));
+ TYPE_SERDES.put(Set.class.getName(), F.t("U.writeCollection(out,
${var})", "U.readSet(in)"));
}
/** Write/Read code for enum. */
private static final IgniteBiTuple<String, String> ENUM_SERDES =
- F.t("U.writeEnum(out, obj.${f});", "U.readEnum(in, ${c}.class)");
+ F.t("U.writeEnum(out, ${var})", "U.readEnum(in, ${Type}.class)");
/** Write/Read code for array. */
private static final IgniteBiTuple<String, String> OBJ_ARRAY_SERDES =
- F.t("U.writeArray(out, obj.${f});", "U.readArray(in, ${c}.class)");
+ F.t("U.writeArray(out, ${var})", "U.readArray(in, ${Type}.class)");
/** Type name to write/read code for the array of type. */
private static final Map<String, IgniteBiTuple<String, String>>
ARRAY_TYPE_SERDES = new HashMap<>();
{
- ARRAY_TYPE_SERDES.put(byte.class.getName(), F.t("U.writeByteArray(out,
obj.${f});", "U.readByteArray(in)"));
- ARRAY_TYPE_SERDES.put(char.class.getName(), F.t("U.writeCharArray(out,
obj.${f});", "U.readCharArray(in)"));
- ARRAY_TYPE_SERDES.put(int.class.getName(), F.t("U.writeIntArray(out,
obj.${f});", "U.readIntArray(in)"));
- ARRAY_TYPE_SERDES.put(long.class.getName(), F.t("U.writeLongArray(out,
obj.${f});", "U.readLongArray(in)"));
- ARRAY_TYPE_SERDES.put(String.class.getName(), OBJ_ARRAY_SERDES);
- ARRAY_TYPE_SERDES.put(UUID.class.getName(), OBJ_ARRAY_SERDES);
+ ARRAY_TYPE_SERDES.put(byte.class.getName(), F.t("U.writeByteArray(out,
${var})", "U.readByteArray(in)"));
+ ARRAY_TYPE_SERDES.put(char.class.getName(), F.t("U.writeCharArray(out,
${var})", "U.readCharArray(in)"));
+ ARRAY_TYPE_SERDES.put(int.class.getName(), F.t("U.writeIntArray(out,
${var})", "U.readIntArray(in)"));
+ ARRAY_TYPE_SERDES.put(long.class.getName(), F.t("U.writeLongArray(out,
${var})", "U.readLongArray(in)"));
}
/** Environment. */
@@ -161,6 +160,9 @@ public class IDTOSerializerGenerator {
/** Serializer imports. */
private final Set<String> imports = new HashSet<>();
+ /** If {@code True} then write method generated now. */
+ private boolean write;
+
/**
* @param env Environment.
* @param type Type to generate serializer for.
@@ -292,12 +294,14 @@ public class IDTOSerializerGenerator {
/** @return Lines for generated {@code
IgniteDataTransferObjectSerializer#writeExternal(T, ObjectOutput)} method. */
private List<String> generateWrite(String clsName,
Collection<VariableElement> flds) {
+ write = true;
+
List<String> code = new ArrayList<>();
code.add("/** {@inheritDoc} */");
code.add("@Override public void writeExternal(" + clsName + " obj,
ObjectOutput out) throws IOException {");
- fieldsSerdes(flds, (t, noNull) -> t.get1()).forEach(line ->
code.add(TAB + line));
+ fieldsSerdes(flds).forEach(line -> code.add(TAB + line));
code.add("}");
@@ -306,36 +310,15 @@ public class IDTOSerializerGenerator {
/** @return Lines for generated {@code
IgniteDataTransferObjectSerializer#readExternal(T, ObjectInput)} method. */
private List<String> generateRead(String clsName,
Collection<VariableElement> flds) {
+ write = false;
+
List<String> code = new ArrayList<>();
code.add("/** {@inheritDoc} */");
code.add("@Override public void readExternal(" + clsName + " obj,
ObjectInput in) throws IOException, ClassNotFoundException {");
- fieldsSerdes(flds, (t, notNull) -> {
- String pattern = "obj.${f} = " + t.get2() + ";";
-
- if (notNull) {
- /**
- * Intention here to change `obj.field = U.readSomething();`
line to:
- * ```
- * Type field0 = U.readSomething();
- * if (field0 != null)
- * obj.field = field0;
- * ```
- * We want to respect @NotNull annotation and keep default
value.
- */
- pattern = pattern.replaceAll("obj.\\$\\{f}", "\\${c} \\${f}0");
- pattern += "\nif (${f}0 != null)\n" + TAB + "obj.${f} =
${f}0;";
- }
- return pattern;
- }).forEach(line -> {
- if (line.indexOf('\n') != -1) {
- for (String line0 : line.split("\n"))
- code.add(TAB + line0);
- }
- else
- code.add(TAB + line);
- });
+ fieldsSerdes(flds).forEach(line -> code.add(TAB + line));
+
code.add("}");
return code;
@@ -343,71 +326,131 @@ public class IDTOSerializerGenerator {
/**
* @param flds Fields to generated serdes for.
- * @param lineProvider Function to generated serdes code for the field.
* @return Lines to serdes fields.
*/
- private List<String> fieldsSerdes(
- Collection<VariableElement> flds,
- BiFunction<IgniteBiTuple<String, String>, Boolean, String> lineProvider
+ private List<String> fieldsSerdes(Collection<VariableElement> flds) {
+ return flds.stream()
+ .flatMap(fld -> variableCode(fld.asType(), "obj." +
fld.getSimpleName().toString()))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * @param type Type of variable.
+ * @param var Name of the variable.
+ */
+ private Stream<String> variableCode(
+ TypeMirror type,
+ String var
) {
TypeMirror dtoCls =
env.getElementUtils().getTypeElement(DTO_CLASS).asType();
- List<String> code = new ArrayList<>();
+ IgniteBiTuple<String, String> serDes = null;
+
+ if (type.getKind() == TypeKind.ARRAY)
+ return arrayCode(type, var);
- for (VariableElement fld : flds) {
- TypeMirror type = fld.asType();
- TypeMirror comp = null;
+ else if (env.getTypeUtils().isAssignable(type, dtoCls))
+ serDes = OBJECT_SERDES;
+ else if (type.getKind() == TypeKind.TYPEVAR)
+ serDes = F.t("out.writeObject(${var})", "in.readObject()");
+ else {
+ if (className(type).equals(Map.class.getName())) {
+ TypeMirror strCls =
env.getElementUtils().getTypeElement(String.class.getName()).asType();
- boolean notNull = type.toString().startsWith("@" +
NotNull.class.getName());
+ DeclaredType dt = (DeclaredType)type;
- IgniteBiTuple<String, String> serDes = null;
+ List<? extends TypeMirror> ta = dt.getTypeArguments();
- if (env.getTypeUtils().isAssignable(type, dtoCls))
- serDes = OBJECT_SERDES;
- else if (type.getKind() == TypeKind.TYPEVAR)
- serDes = F.t("out.writeObject(obj.${f});", "in.readObject()");
- else if (type.getKind() == TypeKind.ARRAY) {
- comp = ((ArrayType)type).getComponentType();
+ if (ta.size() == 2
+ && env.getTypeUtils().isAssignable(ta.get(0), strCls)
+ && env.getTypeUtils().isAssignable(ta.get(1), strCls)) {
+ serDes = STR_STR_MAP;
+ }
+ }
- serDes = ARRAY_TYPE_SERDES.get(className(comp));
+ if (serDes == null) {
+ serDes = TYPE_SERDES.get(className(type));
- if (serDes == null && enumType(env, comp))
- serDes = OBJ_ARRAY_SERDES;
+ if (serDes == null && enumType(env, type))
+ serDes = ENUM_SERDES;
}
- else {
- if (className(type).equals(Map.class.getName())) {
- TypeMirror strCls =
env.getElementUtils().getTypeElement(String.class.getName()).asType();
+ }
+
+ if (serDes == null)
+ throw new IllegalStateException("Unsupported type: " + type);
+
+ boolean notNull = type.toString().startsWith("@" +
NotNull.class.getName());
+
+ Stream<String> code;
+
+ if (notNull && !write) {
+ /**
+ * Intention here to change `obj.field = U.readSomething();` line
to:
+ * ```
+ * {
+ * Type maybeNull = U.readSomething();
+ * if (maybeNull != null)
+ * obj.field = maybeNull;
+ * }
+ * ```
+ * We want to respect @NotNull annotation and keep default value.
+ */
+
+ code = Stream.of("{",
+ TAB + "${Type} maybeNull = " + serDes.get2() + ";",
+ TAB + "if (maybeNull != null)",
+ TAB + TAB + "${var} = maybeNull;",
+ "}");
+ }
+ else
+ code = Stream.of((write ? serDes.get1() : simpleRead(serDes)) +
";");
- DeclaredType dt = (DeclaredType)type;
+ return code.map(line -> replacePlaceholders(line, var, type));
+ }
- List<? extends TypeMirror> ta = dt.getTypeArguments();
+ /**
+ * @param type Array type.
+ * @return Serdes tuple for array.
+ */
+ private Stream<String> arrayCode(TypeMirror type, String var) {
+ TypeMirror comp = ((ArrayType)type).getComponentType();
- if (ta.size() == 2
- && env.getTypeUtils().isAssignable(ta.get(0), strCls)
- && env.getTypeUtils().isAssignable(ta.get(1), strCls))
{
- serDes = STR_STR_MAP;
- }
- }
+ IgniteBiTuple<String, String> arrSerdes =
ARRAY_TYPE_SERDES.get(className(comp));
- if (serDes == null) {
- serDes = TYPE_SERDES.get(className(type));
+ if (arrSerdes == null && !TYPE_SERDES.containsKey(className(comp)) &&
!enumType(env, comp)) {
+ // Ignite can't serialize array element efficiently.
+ arrSerdes = OBJ_ARRAY_SERDES;
+ }
- if (serDes == null && enumType(env, type))
- serDes = ENUM_SERDES;
- }
- }
+ if (arrSerdes != null) {
+ return Stream.of((write ? arrSerdes.get1() :
simpleRead(arrSerdes)) + ";")
+ .map(line -> replacePlaceholders(line, var, comp));
+ }
- if (serDes == null)
- throw new IllegalStateException("Unsupported type: " + type);
+ Stream<String> res;
- String pattern = lineProvider.apply(serDes, notNull);
+ if (write) {
+ res = Stream.of("{",
+ TAB + "int len = ${var} == null ? -1 : ${var}.length;",
+ TAB + "out.writeInt(len);",
+ TAB + "if (len > 0) {",
+ TAB + TAB + "for (int i=0; i<len; i++) {");
- code.add(pattern
- .replaceAll("\\$\\{f}", fld.getSimpleName().toString())
- .replaceAll("\\$\\{c}", simpleClassName(comp == null ? type :
comp)));
+ res = Stream.concat(res, variableCode(comp, var + "[i]").map(line
-> TAB + TAB + TAB + line));
+ res = Stream.concat(res, Stream.of(TAB + TAB + "}", TAB + "}",
"}"));
+ }
+ else {
+ res = Stream.of("{",
+ TAB + "int len = in.readInt();",
+ TAB + "if (len >= 0) {",
+ TAB + TAB + "${var} = new ${Type}[len];",
+ TAB + TAB + "for (int i=0; i<len; i++) {");
+
+ res = Stream.concat(res, variableCode(comp, var + "[i]").map(line
-> TAB + TAB + TAB + line));
+ res = Stream.concat(res, Stream.of(TAB + TAB + "}", TAB + "}",
"}"));
}
- return code;
+ return res.map(line -> replacePlaceholders(line, var, comp));
}
/** @return List of non-static and non-transient field for given {@code
type}. */
@@ -480,7 +523,7 @@ public class IDTOSerializerGenerator {
String fqn = className(type);
- if (!fqn.startsWith("java.lang") && type.getKind() != TypeKind.TYPEVAR)
+ if (!fqn.startsWith("java.lang") && type.getKind() != TypeKind.TYPEVAR
&& type.getKind() != TypeKind.ARRAY)
imports.add(fqn);
return simpleName(fqn);
@@ -490,4 +533,16 @@ public class IDTOSerializerGenerator {
public static String simpleName(String fqn) {
return fqn.substring(fqn.lastIndexOf('.') + 1);
}
+
+ /** Replaces placeholders to current values. */
+ private String replacePlaceholders(String line, String var, TypeMirror
type) {
+ return line
+ .replaceAll("\\$\\{var}", var)
+ .replaceAll("\\$\\{Type}", simpleClassName(type));
+ }
+
+ /** */
+ private static String simpleRead(IgniteBiTuple<String, String> serDes) {
+ return "${var} = " + serDes.get2();
+ }
}