perf[protobuf]: generate pojo in protobuf

This commit is contained in:
godotg
2023-12-03 10:14:22 +08:00
parent a48c453f99
commit bc5bf563fe
3 changed files with 66 additions and 114 deletions
@@ -14,15 +14,16 @@
package com.zfoo.protocol.serializer.protobuf;
import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.serializer.protobuf.codegen.IfaceGenerator;
import com.zfoo.protocol.serializer.protobuf.builder.JavaBuilder;
import com.zfoo.protocol.serializer.protobuf.wire.Option;
import com.zfoo.protocol.serializer.protobuf.wire.ProtoMessage;
import com.zfoo.protocol.serializer.protobuf.wire.parser.Proto;
import com.zfoo.protocol.serializer.protobuf.wire.parser.ProtoParser;
import com.zfoo.protocol.util.FileUtils;
import com.zfoo.protocol.util.StringUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
public class GeneratePbUtils {
@@ -41,11 +42,8 @@ public class GeneratePbUtils {
throw new RuntimeException(StringUtils.format("There are no proto files to build in proto path:[{}]", buildOption.getProtoPath()));
}
var srcPath = new File(buildOption.getOutputPath());
var protos = parseProtoFile(protoFiles);
IfaceGenerator ifaceGenerator = new IfaceGenerator(srcPath, protos);
ifaceGenerator.generate();
generate(buildOption, protos);
}
public static List<Proto> parseProtoFile(List<File> protoFiles) {
@@ -67,4 +65,51 @@ public class GeneratePbUtils {
return protos;
}
public static void generate(PbBuildOption buildOption, List<Proto> protos) {
Map<String, Proto> allProtos = new HashMap<>();
for (Proto proto : protos) {
allProtos.put(proto.getName(), proto);
}
for (var proto : protos) {
List<Option> options = proto.getOptions();
Map<String, String> protoOptions = new HashMap<>();
if (options != null) {
options.forEach(o -> protoOptions.put(o.getName(), o.getValue()));
}
generateDtoMessage(buildOption, proto, allProtos);
}
}
private static void generateDtoMessage(PbBuildOption buildOption, Proto proto, Map<String, Proto> protos) {
JavaBuilder builder = new JavaBuilder();
Map<String, String> msgComments = new HashMap<>();
List<ProtoMessage> msgs = proto.getMessages();
if (CollectionUtils.isNotEmpty(msgs)) {
String msgPath = buildOption.getOutputPath() + File.separator;
msgs.stream()
.sorted(Comparator.comparing(ProtoMessage::getName))
.forEach(it -> {
StringBuilder mc = new StringBuilder();
if (it.getComment() != null) {
mc.append(it.getComment());
} else {
if (it.getComment() != null && it.getComment().getLines() != null) {
it.getComment().getLines().forEach(c -> mc.append(c));
}
}
msgComments.put(it.getName(), mc.toString());
var code = builder.buildMessage(proto, it, 1, null, protos);
var filePath = msgPath + File.separator + it.getName() + ".java";
FileUtils.writeStringToFile(new File(filePath), code, false);
});
}
}
}
@@ -47,7 +47,7 @@ public class JavaBuilder {
return jType;
}
public String getJavaType(Field field, List<String> imps, PbBuildOption buildOps) {
public String getJavaType(Field field, List<String> imps) {
String type = field.getTypeString();
if (field instanceof MapField) {
MapField mf = (MapField) field;
@@ -120,11 +120,10 @@ public class JavaBuilder {
}
}
private void buildMsgImps(ProtoMessage msg, List<Field> tmp, List<String> imps,
PbBuildOption buildOps) {
private void buildMsgImps(ProtoMessage msg, List<Field> tmp, List<String> imps) {
if (msg.getFields() != null) {
msg.getFields().forEach(e -> {
getJavaType(e, imps, buildOps);
getJavaType(e, imps);
tmp.add(e);
});
}
@@ -160,21 +159,21 @@ public class JavaBuilder {
return StringUtils.EMPTY;
}
public String buildMessage(Proto proto, ProtoMessage msg, int indent, Map<String, String> defineMsgs, PbBuildOption buildOps, Map<String, Proto> protos) {
public String buildMessage(Proto proto, ProtoMessage msg, int indent, Map<String, String> defineMsgs, Map<String, Proto> protos) {
int level = Math.max(indent, 1);
final CodeBuilder cb = new CodeBuilder();
List<Field> tmp = new ArrayList<>();
List<String> imps = new ArrayList<>();
buildMsgImps(msg, tmp, imps, buildOps);
buildMsgImps(msg, tmp, imps);
List<Field> fields = new ArrayList<>();
tmp.stream().sorted(Comparator.comparingInt(Field::getTag))
.forEach(fields::add);
if (!buildOps.isIsNested()) {
imps.stream().sorted(Comparator.naturalOrder())
.forEach(e -> cb.t(level - 1).e("import $cls$;").arg(e).ln());
}
// not nested
imps.stream().sorted(Comparator.naturalOrder())
.forEach(e -> cb.t(level - 1).e("import $cls$;").arg(e).ln());
cb.ln();
buildDocComment(cb, msg.getComment(), level - 1);
cb.t(level - 1).e("public class $name$ {").arg(msg.getName()).ln(2);
@@ -198,10 +197,10 @@ public class JavaBuilder {
typeName = Proto.class.getPackage().getName() + ".wire.Field." + typeName;
}
buildDocComment(cb, f.getComment(), level);
String type = getJavaType(f, imps, buildOps);
String type = getJavaType(f, imps);
String name = f.getName();
if (f.getCardinality() == Field.Cardinality.REPEATED) {
String boxedTypeName = getBoxedTypeName(f, buildOps);
String boxedTypeName = getBoxedTypeName(f);
type = "List<" + boxedTypeName + ">";
}
@@ -249,8 +248,8 @@ public class JavaBuilder {
return cb.toString();
}
private String getBoxedTypeName(Field f, PbBuildOption buildOps) {
String type = getJavaType(f, null, buildOps);
private String getBoxedTypeName(Field f) {
String type = getJavaType(f, null);
if (BASE_TYPES.contains(f.getTypeString())) {
JavaType javaType = null;
javaType = Type.valueOf(f.getTypeString().toUpperCase(Locale.ENGLISH)).javaType();
@@ -269,10 +268,8 @@ public class JavaBuilder {
return;
}
cb.ln();
PbBuildOption nestedOps = new PbBuildOption();
nestedOps.setIsNested(true);
msgs.stream().sorted(Comparator.comparing(ProtoMessage::getName))
.forEach(e -> cb.c(buildMessage(proto, e, level + 1, defineMsgs, nestedOps, protos)));
.forEach(e -> cb.c(buildMessage(proto, e, level + 1, defineMsgs, protos)));
}
@@ -1,90 +0,0 @@
/*
* Copyright 2021 The edap Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the License.
*/
package com.zfoo.protocol.serializer.protobuf.codegen;
import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.serializer.protobuf.PbBuildOption;
import com.zfoo.protocol.serializer.protobuf.builder.JavaBuilder;
import com.zfoo.protocol.serializer.protobuf.wire.Option;
import com.zfoo.protocol.serializer.protobuf.wire.ProtoMessage;
import com.zfoo.protocol.serializer.protobuf.wire.parser.Proto;
import com.zfoo.protocol.util.FileUtils;
import java.io.File;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class IfaceGenerator {
private final File srcPath;
private final List<Proto> files;
public IfaceGenerator(File srcPath, List<Proto> files) {
this.srcPath = srcPath;
this.files = files;
}
public void generate() {
List<Proto> protos = files;
Map<String, Proto> allProtos = new HashMap<>();
for (Proto proto : protos) {
allProtos.put(proto.getName(), proto);
}
protos.forEach(p -> {
generateProtoItem(p, allProtos);
});
}
private void generateProtoItem(Proto proto, Map<String, Proto> protos) {
List<Option> options = proto.getOptions();
Map<String, String> protoOptions = new HashMap<>();
if (options != null) {
options.forEach(o -> protoOptions.put(o.getName(), o.getValue()));
}
PbBuildOption buildOps = new PbBuildOption();
generateDtoMessage(proto, buildOps, protos);
}
public void generateDtoMessage(Proto proto, PbBuildOption buildOps, Map<String, Proto> protos) {
JavaBuilder builder = new JavaBuilder();
Map<String, String> msgComments = new HashMap<>();
List<ProtoMessage> msgs = proto.getMessages();
if (CollectionUtils.isNotEmpty(msgs)) {
String msgPath = srcPath + File.separator;
msgs.stream()
.sorted(Comparator.comparing(ProtoMessage::getName))
.forEach(it -> {
StringBuilder mc = new StringBuilder();
if (it.getComment() != null) {
mc.append(it.getComment());
} else {
if (it.getComment() != null && it.getComment().getLines() != null) {
it.getComment().getLines().forEach(c -> mc.append(c));
}
}
msgComments.put(it.getName(), mc.toString());
var code = builder.buildMessage(proto, it, 1, null, buildOps, protos);
var filePath = msgPath + File.separator + it.getName() + ".java";
FileUtils.writeStringToFile(new File(filePath), code, false);
});
}
}
}