-
Notifications
You must be signed in to change notification settings - Fork 1.9k
IGNITE-27709 Generate serdes code for IgniteDataTransferObject #12677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
39f11fa
3f15eb9
ab1ff1a
45720ab
3770237
bc0e9f0
6959ecc
308ddd6
baa6d7b
d58705f
2f51d3d
bea02ab
1913eca
9d9e5b8
c78d3ec
bcb334f
14338c1
5bc0e15
3945a26
2f65874
6d9b2d2
336babe
2f615b4
0fa3a41
46ba939
d3b5fb8
4273d96
daf82fb
f8bcd75
51be989
5fd25f7
132bcc2
fc043fd
19d758a
7a4c425
4535fd9
52f1619
aa5fe15
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,11 +27,14 @@ | |
| import java.io.StringWriter; | ||
| import java.io.Writer; | ||
| import java.util.ArrayList; | ||
| import java.util.Collection; | ||
| import java.util.HashMap; | ||
| import java.util.HashSet; | ||
| import java.util.LinkedHashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
| import java.util.TreeMap; | ||
| import java.util.UUID; | ||
| import java.util.function.Function; | ||
| import javax.annotation.processing.FilerException; | ||
|
|
@@ -44,6 +47,7 @@ | |
| import javax.lang.model.element.TypeElement; | ||
| import javax.lang.model.element.VariableElement; | ||
| import javax.lang.model.type.ArrayType; | ||
| import javax.lang.model.type.DeclaredType; | ||
| import javax.lang.model.type.PrimitiveType; | ||
| import javax.lang.model.type.TypeKind; | ||
| import javax.lang.model.type.TypeMirror; | ||
|
|
@@ -56,6 +60,7 @@ | |
| import static org.apache.ignite.internal.MessageSerializerGenerator.TAB; | ||
| import static org.apache.ignite.internal.MessageSerializerGenerator.enumType; | ||
| import static org.apache.ignite.internal.MessageSerializerGenerator.identicalFileIsAlreadyGenerated; | ||
| import static org.apache.ignite.internal.idto.IgniteDataTransferObjectProcessor.DTO_CLASS; | ||
|
|
||
| /** | ||
| * Generates serializer class for given {@code IgniteDataTransferObject} extension. | ||
|
|
@@ -66,13 +71,21 @@ public class IDTOSerializerGenerator { | |
| /** Serializer interface. */ | ||
| public static final String DTO_SERDES_INTERFACE = "org.apache.ignite.internal.dto.IgniteDataTransferObjectSerializer"; | ||
|
|
||
| /** Class javadoc */ | ||
| /** Class javadoc. */ | ||
| static final String CLS_JAVADOC = "/** " + NL + | ||
| " * This class is generated automatically." + NL + | ||
| " *" + NL + | ||
| " * @see org.apache.ignite.internal.dto.IgniteDataTransferObject" + NL + | ||
| " */"; | ||
|
|
||
| /** */ | ||
| private static final IgniteBiTuple<String, String> OBJECT_SERDES = | ||
| F.t("out.writeObject(obj.${f});", "obj.${f} = (${c})in.readObject();"); | ||
|
|
||
| /** */ | ||
| private static final IgniteBiTuple<String, String> STR_STR_MAP = | ||
| F.t("U.writeStringMap(out, obj.${f});", "obj.${f} = U.readStringMap(in);"); | ||
|
|
||
| /** Type name to write/read code for the type. */ | ||
| private static final Map<String, IgniteBiTuple<String, String>> TYPE_SERDES = new HashMap<>(); | ||
|
|
||
|
|
@@ -85,22 +98,34 @@ public class IDTOSerializerGenerator { | |
| TYPE_SERDES.put(float.class.getName(), F.t("out.writeFloat(obj.${f});", "obj.${f} = in.readFloat();")); | ||
| TYPE_SERDES.put(double.class.getName(), F.t("out.writeDouble(obj.${f});", "obj.${f} = in.readDouble();")); | ||
|
|
||
| IgniteBiTuple<String, String> objSerdes = F.t("out.writeObject(obj.${f});", "obj.${f} = (${c})in.readObject();"); | ||
|
|
||
| TYPE_SERDES.put(Boolean.class.getName(), objSerdes); | ||
| TYPE_SERDES.put(Byte.class.getName(), objSerdes); | ||
| TYPE_SERDES.put(Short.class.getName(), objSerdes); | ||
| TYPE_SERDES.put(Integer.class.getName(), objSerdes); | ||
| TYPE_SERDES.put(Long.class.getName(), objSerdes); | ||
| TYPE_SERDES.put(Float.class.getName(), objSerdes); | ||
| TYPE_SERDES.put(Double.class.getName(), objSerdes); | ||
| TYPE_SERDES.put(Boolean.class.getName(), OBJECT_SERDES); | ||
| TYPE_SERDES.put(Byte.class.getName(), OBJECT_SERDES); | ||
| TYPE_SERDES.put(Short.class.getName(), OBJECT_SERDES); | ||
| TYPE_SERDES.put(Integer.class.getName(), OBJECT_SERDES); | ||
| TYPE_SERDES.put(Long.class.getName(), OBJECT_SERDES); | ||
| TYPE_SERDES.put(Float.class.getName(), OBJECT_SERDES); | ||
| TYPE_SERDES.put(Double.class.getName(), OBJECT_SERDES); | ||
| TYPE_SERDES.put(Throwable.class.getName(), OBJECT_SERDES); | ||
| 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});", "obj.${f} = U.readString(in);")); | ||
| TYPE_SERDES.put(UUID.class.getName(), F.t("U.writeUuid(out, obj.${f});", "obj.${f} = U.readUuid(in);")); | ||
| TYPE_SERDES.put("org.apache.ignite.lang.IgniteUuid", F.t("U.writeIgniteUuid(out, obj.${f});", "obj.${f} = U.readIgniteUuid(in);")); | ||
| TYPE_SERDES.put("org.apache.ignite.internal.processors.cache.version.GridCacheVersion", objSerdes); | ||
|
|
||
| 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); | ||
| TYPE_SERDES.put("org.apache.ignite.internal.management.cache.PartitionKey", OBJECT_SERDES); | ||
| TYPE_SERDES.put("org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion", OBJECT_SERDES); | ||
| TYPE_SERDES.put("org.apache.ignite.cache.CacheMode", | ||
| F.t("out.writeByte(CacheMode.toCode(obj.${f}));", "obj.${f} = CacheMode.fromCode(in.readByte());")); | ||
|
|
||
| TYPE_SERDES.put(TreeMap.class.getName(), F.t("U.writeMap(out, obj.${f});", "obj.${f} = U.readTreeMap(in);")); | ||
| TYPE_SERDES.put(LinkedHashMap.class.getName(), F.t("U.writeMap(out, obj.${f});", "obj.${f} = U.readLinkedMap(in);")); | ||
| TYPE_SERDES.put(Map.class.getName(), F.t("U.writeMap(out, obj.${f});", "obj.${f} = U.readMap(in);")); | ||
| TYPE_SERDES.put(Collection.class.getName(), F.t("U.writeCollection(out, obj.${f});", "obj.${f} = U.readCollection(in);")); | ||
| TYPE_SERDES.put(List.class.getName(), F.t("U.writeCollection(out, obj.${f});", "obj.${f} = U.readList(in);")); | ||
| TYPE_SERDES.put(Set.class.getName(), F.t("U.writeCollection(out, obj.${f});", "obj.${f} = U.readSet(in);")); | ||
| } | ||
|
|
||
| /** Write/Read code for enum. */ | ||
|
|
@@ -295,21 +320,50 @@ private List<String> fieldsSerdes( | |
| List<VariableElement> flds, | ||
| Function<IgniteBiTuple<String, String>, String> lineProvider | ||
| ) { | ||
| TypeMirror dtoCls = env.getElementUtils().getTypeElement(DTO_CLASS).asType(); | ||
|
|
||
| List<String> code = new ArrayList<>(); | ||
|
|
||
| for (VariableElement fld : flds) { | ||
| TypeMirror type = fld.asType(); | ||
| TypeMirror comp = null; | ||
|
|
||
| IgniteBiTuple<String, String> serDes; | ||
| IgniteBiTuple<String, String> serDes = null; | ||
|
|
||
| if (type.getKind() == TypeKind.ARRAY) { | ||
| if (env.getTypeUtils().isAssignable(type, dtoCls)) | ||
| serDes = OBJECT_SERDES; | ||
| else if (type.getKind() == TypeKind.TYPEVAR) | ||
| serDes = F.t("out.writeObject(obj.${f});", "obj.${f} = in.readObject();"); | ||
| else if (type.getKind() == TypeKind.ARRAY) { | ||
| comp = ((ArrayType)type).getComponentType(); | ||
|
|
||
| serDes = enumType(env, comp) ? OBJ_ARRAY_SERDES : ARRAY_TYPE_SERDES.get(className(comp)); | ||
| serDes = ARRAY_TYPE_SERDES.get(className(comp)); | ||
|
|
||
| if (serDes == null && enumType(env, comp)) | ||
| serDes = OBJ_ARRAY_SERDES; | ||
| } | ||
| else { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets use elseif instead and check at final else that there is no enexpected type. |
||
| if (className(type).equals(Map.class.getName())) { | ||
| TypeMirror strCls = env.getElementUtils().getTypeElement(String.class.getName()).asType(); | ||
|
|
||
| DeclaredType dt = (DeclaredType)type; | ||
|
|
||
| List<? extends TypeMirror> ta = dt.getTypeArguments(); | ||
|
|
||
| if (ta.size() == 2 | ||
| && env.getTypeUtils().isAssignable(ta.get(0), strCls) | ||
| && env.getTypeUtils().isAssignable(ta.get(1), strCls)) { | ||
| serDes = STR_STR_MAP; | ||
| } | ||
| } | ||
|
|
||
| if (serDes == null) { | ||
| serDes = TYPE_SERDES.get(className(type)); | ||
|
|
||
| if (serDes == null && enumType(env, type)) | ||
| serDes = ENUM_SERDES; | ||
| } | ||
| } | ||
| else | ||
| serDes = enumType(env, type) ? ENUM_SERDES : TYPE_SERDES.get(className(type)); | ||
|
|
||
| if (serDes != null) { | ||
| String pattern = lineProvider.apply(serDes); | ||
|
|
@@ -351,6 +405,12 @@ private List<VariableElement> fields(TypeElement type) { | |
| /** @return FQN of {@code comp}. */ | ||
| private static String className(TypeMirror comp) { | ||
| String n = comp.toString(); | ||
|
|
||
| int spaceIdx = n.indexOf(' '); | ||
|
|
||
| if (spaceIdx != -1) | ||
| n = n.substring(spaceIdx + 1); | ||
|
|
||
| int genIdx = n.indexOf('<'); | ||
|
|
||
| return genIdx == -1 ? n : n.substring(0, genIdx); | ||
|
|
@@ -372,7 +432,7 @@ private String simpleClassName(TypeMirror type) { | |
|
|
||
| String fqn = className(type); | ||
|
|
||
| if (!fqn.startsWith("java.lang")) | ||
| if (!fqn.startsWith("java.lang") && type.getKind() != TypeKind.TYPEVAR) | ||
| imports.add(fqn); | ||
|
|
||
| return simpleName(fqn); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,7 +33,6 @@ | |
| import javax.annotation.processing.SupportedAnnotationTypes; | ||
| import javax.annotation.processing.SupportedSourceVersion; | ||
| import javax.lang.model.SourceVersion; | ||
| import javax.lang.model.element.AnnotationMirror; | ||
| import javax.lang.model.element.Element; | ||
| import javax.lang.model.element.ElementKind; | ||
| import javax.lang.model.element.Modifier; | ||
|
|
@@ -62,23 +61,32 @@ public class IgniteDataTransferObjectProcessor extends AbstractProcessor { | |
| /** Package for serializers. */ | ||
| private static final String FACTORY_PKG_NAME = "org.apache.ignite.internal.codegen.idto"; | ||
|
|
||
| /** Base class that every dto must extend. */ | ||
| private static final String DTO_CLASS = "org.apache.ignite.internal.dto.IgniteDataTransferObject"; | ||
|
|
||
| /** | ||
| * Annotation used in management commands. | ||
| * For now, we restrict set of generated serdes to all management commands argument classes. | ||
| * Because, they strictly follows Ignite codestyle convention. | ||
| * Providing support of all other inheritor of {@code IgniteDataTransferObject} is matter of following improvements. | ||
| */ | ||
| private static final String ARG_ANNOTATION = "org.apache.ignite.internal.management.api.Argument"; | ||
| /** Base class that every dto must extends. */ | ||
| static final String DTO_CLASS = "org.apache.ignite.internal.dto.IgniteDataTransferObject"; | ||
|
|
||
| /** Factory class name. */ | ||
| public static final String FACTORY_CLASS = "IDTOSerializerFactory"; | ||
|
|
||
| /** Generated classes. */ | ||
| private final Map<TypeElement, String> genSerDes = new HashMap<>(); | ||
|
|
||
| /** Currently unsupported classes. */ | ||
| private static final Set<String> UNSUPPORTED = Set.of( | ||
| "org.apache.ignite.internal.management.baseline.BaselineNode", | ||
| "org.apache.ignite.internal.processors.cache.CacheMetricsSnapshot", | ||
| "org.apache.ignite.internal.commandline.cache.check_indexes_inline_size.CheckIndexInlineSizesResult", | ||
| "org.apache.ignite.internal.management.cache.ContentionJobResult", | ||
| "org.apache.ignite.internal.processors.metastorage.persistence.DistributedMetaStorageHistoryItem", | ||
| "org.apache.ignite.internal.management.tx.TxInfo", | ||
| "org.apache.ignite.internal.management.encryption.ReencryptionSuspendTask.ReencryptionSuspendResumeJobResult", | ||
| "org.apache.ignite.internal.management.encryption.ReencryptionStatusTask.ReencryptionStatusResult", | ||
| "org.apache.ignite.internal.management.encryption.EncryptionKeyIdsTask.EncryptionKeyIdsResult", | ||
| "org.apache.ignite.internal.management.cache.IndexForceRebuildTaskRes", | ||
| "org.apache.ignite.internal.management.cache.IndexRebuildStatusInfoContainer", | ||
| "org.apache.ignite.internal.visor.VisorTaskResult", | ||
| "org.apache.ignite.internal.visor.VisorTaskArgument" | ||
| ); | ||
|
|
||
| /** | ||
| * Processes all classes extending the {@code IgniteDataTransferObject} and generates corresponding serializer code. | ||
| */ | ||
|
|
@@ -107,28 +115,27 @@ private void generateSingle(Element el) { | |
| return; | ||
|
|
||
| TypeMirror dtoCls = processingEnv.getElementUtils().getTypeElement(DTO_CLASS).asType(); | ||
| TypeMirror argAnnotation = processingEnv.getElementUtils().getTypeElement(ARG_ANNOTATION).asType(); | ||
|
|
||
| TypeElement clazz = (TypeElement)el; | ||
|
|
||
| // Generate code for inner classes. | ||
| clazz.getEnclosedElements().forEach(this::generateSingle); | ||
|
|
||
| if (UNSUPPORTED.contains(clazz.getQualifiedName().toString())) | ||
| return; | ||
|
|
||
| if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), dtoCls)) | ||
| return; | ||
|
|
||
| if (clazz.getModifiers().contains(Modifier.ABSTRACT)) | ||
| return; | ||
|
|
||
| if (!clazz.getModifiers().contains(Modifier.PUBLIC)) | ||
| if (clazz.getModifiers().contains(Modifier.PRIVATE) && clazz.getModifiers().contains(Modifier.PROTECTED)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about to use some annotation instead of "package field" mark?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I don't understand the question
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now you're using "package field" mark as a marker that field shoul be serialized. Let's use annotation instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are commenting code part thats filter out classes, not fields. |
||
| return; | ||
|
|
||
| if (clazz.getNestingKind() != NestingKind.TOP_LEVEL && clazz.getNestingKind() != NestingKind.MEMBER) | ||
| return; | ||
|
|
||
| if (!hasArgumentFields(clazz, argAnnotation)) | ||
| return; | ||
|
|
||
| try { | ||
| IDTOSerializerGenerator gen = new IDTOSerializerGenerator(processingEnv, clazz); | ||
|
|
||
|
|
@@ -364,27 +371,4 @@ private void serializer(Writer writer) throws IOException { | |
| writer.write("}"); | ||
| writer.write(NL); | ||
| } | ||
|
|
||
| /** | ||
| * @param type Type to analyze. | ||
| * @param argAnnotation Annotation to find. | ||
| * @return {@code True} if type has fields annotated with the {@code argAnnotation}, {@code false} otherwise. | ||
| */ | ||
| private boolean hasArgumentFields(TypeElement type, TypeMirror argAnnotation) { | ||
| while (type != null) { | ||
| for (Element el: type.getEnclosedElements()) { | ||
| if (el.getKind() != ElementKind.FIELD) | ||
| continue; | ||
|
|
||
| for (AnnotationMirror am : el.getAnnotationMirrors()) { | ||
| if (am.getAnnotationType().equals(argAnnotation)) | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| type = (TypeElement)processingEnv.getTypeUtils().asElement(type.getSuperclass()); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about to write classname or specify type at annotation or smth similar instead of changing the type of the field to TreeMap?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having field type as TreeMap is enough