Skip to content

Commit 307374c

Browse files
IGNITE-26728 Perform enum fields serialization/deserialization with mappers
1 parent 3d73836 commit 307374c

File tree

6 files changed

+289
-30
lines changed

6 files changed

+289
-30
lines changed

modules/codegen2/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ class MessageSerializerGenerator {
9292
/** Collection of message-specific imports. */
9393
private final Set<String> imports = new TreeSet<>();
9494

95+
/** Collection of Serializer class fields containing mappers for message enum fields. */
96+
private final Set<String> mapperFields = new TreeSet<>();
97+
9598
/** */
9699
private final ProcessingEnvironment env;
97100

@@ -142,6 +145,8 @@ private String generateSerializerCode(String serClsName) throws IOException {
142145
try (Writer writer = new StringWriter()) {
143146
writeClassHeader(writer, PKG_NAME, serClsName);
144147

148+
writeClassFields(writer);
149+
145150
// Write #writeTo method.
146151
for (String w: write)
147152
writer.write(w + NL);
@@ -238,10 +243,6 @@ private void processField(VariableElement field, int opt) throws Exception {
238243
if (assignableFrom(field.asType(), type(Throwable.class.getName())))
239244
throw new UnsupportedOperationException("You should use ErrorMessage for serialization of throwables.");
240245

241-
if (enumType(erasedType(field.asType())))
242-
throw new IllegalArgumentException("Unsupported enum type: " + field.asType() +
243-
". The enum must be wrapped into a Message (see, for example, TransactionIsolationMessage).");
244-
245246
writeField(field, opt);
246247
readField(field, opt);
247248
}
@@ -335,7 +336,7 @@ private void returnFalseIfWriteFailed(VariableElement field) throws Exception {
335336

336337
imports.add("org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType");
337338

338-
returnFalseIfWriteFailed(write, "writer.writeObjectArray", getExpr,
339+
returnFalseIfEnumWriteFailed(write, "writer.writeObjectArray", getExpr,
339340
"MessageCollectionItemType." + messageCollectionItemType(componentType));
340341

341342
return;
@@ -388,10 +389,31 @@ else if (assignableFrom(erasedType(type), type(Collection.class.getName()))) {
388389

389390
imports.add("org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType");
390391

391-
returnFalseIfWriteFailed(write, "writer.writeCollection", getExpr,
392+
returnFalseIfEnumWriteFailed(write, "writer.writeCollection", getExpr,
392393
"MessageCollectionItemType." + messageCollectionItemType(typeArgs.get(0)));
393394
}
394395

396+
else if (enumType(type)) {
397+
//TODO check CustomMapper annotation
398+
imports.add("org.apache.ignite.plugin.extensions.communication.mappers.EnumMapper");
399+
imports.add("org.apache.ignite.plugin.extensions.communication.mappers.DefaultEnumMapper");
400+
401+
Element element = env.getTypeUtils().asElement(type);
402+
403+
imports.add(element.toString());
404+
405+
String enumName = element.getSimpleName().toString();
406+
407+
char[] chars = enumName.toCharArray();
408+
chars[0] = Character.toLowerCase(chars[0]);
409+
410+
String enumMapperSerializerFieldName = new String(chars) + "Mapper";
411+
412+
mapperFields.add("private final EnumMapper<" + enumName + "> " + enumMapperSerializerFieldName + " = new DefaultEnumMapper<>(" + enumName + ".values());");
413+
414+
returnFalseIfEnumWriteFailed(write, "writer.writeByte", enumMapperSerializerFieldName + ".encode", getExpr);
415+
}
416+
395417
else
396418
throw new IllegalArgumentException("Unsupported declared type: " + type);
397419

@@ -420,6 +442,23 @@ private void returnFalseIfWriteFailed(Collection<String> code, String accessor,
420442
indent--;
421443
}
422444

445+
/**
446+
* Generate code of writing single enum field mapped with EnumMapper:
447+
* <pre>
448+
* if (!writer.writeByte(myEnumMapper.encode(msg.myEnum()))
449+
* return false;
450+
* </pre>
451+
*/
452+
private void returnFalseIfEnumWriteFailed(Collection<String> code, String writerCall, String mapperCall, String fieldGetterCall) {
453+
code.add(line("if (!%s(%s(msg.%s)))", writerCall, mapperCall, fieldGetterCall));
454+
455+
indent++;
456+
457+
code.add(line("return false;"));
458+
459+
indent--;
460+
}
461+
423462
/**
424463
* Discover access read methods, like {@code readInt}.
425464
*
@@ -530,6 +569,18 @@ else if (assignableFrom(erasedType(type), type(Collection.class.getName()))) {
530569
"MessageCollectionItemType." + messageCollectionItemType(typeArgs.get(0)));
531570
}
532571

572+
else if (enumType(type)) {
573+
//TODO check CustomMapper annotation
574+
String enumName = env.getTypeUtils().asElement(type).getSimpleName().toString();
575+
576+
char[] chars = enumName.toCharArray();
577+
chars[0] = Character.toLowerCase(chars[0]);
578+
579+
String enumMapperSerializerFieldName = new String(chars) + "Mapper";
580+
581+
returnFalseIfEnumReadFailed(name, enumMapperSerializerFieldName + ".decode");
582+
}
583+
533584
else
534585
throw new IllegalArgumentException("Unsupported declared type: " + type);
535586

@@ -643,6 +694,32 @@ private void returnFalseIfReadFailed(String var, String mtd, String... args) {
643694
indent--;
644695
}
645696

697+
/**
698+
* Generate code of reading single field:
699+
* <pre>
700+
* msg.id(reader.readInt());
701+
*
702+
* if (!reader.isLastRead())
703+
* return false;
704+
* </pre>
705+
*
706+
* @param msgSetterName Variable name.
707+
* @param mapperDecodeCall Method name.
708+
*/
709+
private void returnFalseIfEnumReadFailed(String msgSetterName, String mapperDecodeCall) {
710+
read.add(line("msg.%s(%s(reader.readByte()));", msgSetterName, mapperDecodeCall));
711+
712+
read.add(EMPTY);
713+
714+
read.add(line("if (!reader.isLastRead())"));
715+
716+
indent++;
717+
718+
read.add(line("return false;"));
719+
720+
indent--;
721+
}
722+
646723
/** */
647724
private void finish(List<String> code) {
648725
String lastLine = code.get(code.size() - 1);
@@ -672,6 +749,21 @@ private String line(String format, Object... args) {
672749
return sb.toString();
673750
}
674751

752+
/** Write header of serializer class: license, imports, class declaration. */
753+
private void writeClassFields(Writer writer) throws IOException {
754+
indent = 1;
755+
756+
for (String field: mapperFields) {
757+
writer.write(line(METHOD_JAVADOC));
758+
writer.write(NL);
759+
writer.write(line(field));
760+
writer.write(NL);
761+
}
762+
writer.write(NL);
763+
764+
indent = 0;
765+
}
766+
675767
/** Write header of serializer class: license, imports, class declaration. */
676768
private void writeClassHeader(Writer writer, String pkgName, String serClsName) throws IOException {
677769
try (InputStream in = getClass().getClassLoader().getResourceAsStream("license.txt");
@@ -719,13 +811,9 @@ private boolean assignableFrom(TypeMirror type, TypeMirror superType) {
719811

720812
/** */
721813
private boolean enumType(TypeMirror type) {
722-
if (type.getKind() == TypeKind.DECLARED) {
723-
Element element = env.getTypeUtils().asElement(type);
724-
725-
return element != null && element.getKind() == ElementKind.ENUM;
726-
}
814+
Element element = env.getTypeUtils().asElement(type);
727815

728-
return false;
816+
return element != null && element.getKind() == ElementKind.ENUM;
729817
}
730818

731819
/** */

modules/core/src/test/resources/codegen/UnwrappedEnumFieldMessage.java renamed to modules/core/src/main/java/org/apache/ignite/plugin/extensions/communication/mappers/DefaultEnumMapper.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,25 @@
1515
* limitations under the License.
1616
*/
1717

18-
package org.apache.ignite.internal;
18+
package org.apache.ignite.plugin.extensions.communication.mappers;
1919

20-
import org.apache.ignite.plugin.extensions.communication.Message;
21-
import org.apache.ignite.transactions.TransactionIsolation;
20+
/** */
21+
public class DefaultEnumMapper<T extends Enum<T>> implements EnumMapper<T> {
22+
/** */
23+
private final T[] enumVals;
2224

23-
public class UnwrappedEnumFieldMessage implements Message {
24-
@Order(0)
25-
private TransactionIsolation isolation;
26-
27-
public TransactionIsolation isolation() {
28-
return isolation;
25+
/** */
26+
public DefaultEnumMapper(T[] vals) {
27+
enumVals = vals;
2928
}
3029

31-
public void isolation(TransactionIsolation isolation) {
32-
this.isolation = isolation;
30+
/** {@inheritDoc} */
31+
@Override public byte encode(T enumVal) {
32+
return (byte)enumVal.ordinal();
3333
}
3434

35-
public short directType() {
36-
return 0;
35+
/** {@inheritDoc} */
36+
@Override public T decode(byte enumCode) {
37+
return enumVals[enumCode];
3738
}
3839
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ignite.plugin.extensions.communication.mappers;
19+
20+
public interface EnumMapper<T extends Enum<T>> {
21+
public byte encode(T enumVal);
22+
23+
public T decode(byte enumCode);
24+
}

modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import org.apache.ignite.internal.util.typedef.F;
3131
import org.apache.ignite.lang.IgniteUuid;
3232
import org.apache.ignite.plugin.extensions.communication.Message;
33-
import org.apache.ignite.transactions.TransactionIsolation;
3433
import org.junit.Test;
3534

3635
import static com.google.testing.compile.CompilationSubject.assertThat;
@@ -173,11 +172,14 @@ public void testExceptionFailed() {
173172

174173
/** */
175174
@Test
176-
public void testEnumFieldFailed() {
177-
Compilation compilation = compile("UnwrappedEnumFieldMessage.java");
175+
public void testEnumFields() {
176+
Compilation compilation = compile("EnumFieldsMessage.java");
178177

179-
assertThat(compilation).failed();
180-
assertThat(compilation).hadErrorContaining("Unsupported enum type: " + TransactionIsolation.class.getName());
178+
assertThat(compilation).succeeded();
179+
180+
assertThat(compilation)
181+
.generatedSourceFile("org.apache.ignite.internal.codegen.EnumFieldsMessageSerializer")
182+
.hasSourceEquivalentTo(javaFile("EnumFieldsMessageSerializer.java"));
181183
}
182184

183185
/** */
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ignite.internal;
19+
20+
import org.apache.ignite.cache.CacheAtomicityMode;
21+
import org.apache.ignite.internal.processors.cache.GridCacheOperation;
22+
import org.apache.ignite.plugin.extensions.communication.Message;
23+
import org.apache.ignite.transactions.TransactionIsolation;
24+
25+
public class EnumFieldsMessage implements Message {
26+
@Order(0)
27+
private TransactionIsolation publicEnum;
28+
29+
@Order(1)
30+
private GridCacheOperation internalEnum;
31+
32+
public TransactionIsolation publicEnum() {
33+
return publicEnum;
34+
}
35+
36+
public void publicEnum(TransactionIsolation publicEnum) {
37+
this.publicEnum = publicEnum;
38+
}
39+
40+
public GridCacheOperation internalEnum() {
41+
return internalEnum;
42+
}
43+
44+
public void internalEnum(GridCacheOperation internalEnum) {
45+
this.internalEnum = internalEnum;
46+
}
47+
48+
public short directType() {
49+
return 0;
50+
}
51+
}

0 commit comments

Comments
 (0)