mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-05-23 14:25:55 +00:00
feat[protobuf]: oneProtocol param, all protocol files are generated in a single protocol file
This commit is contained in:
@@ -26,6 +26,8 @@ import com.zfoo.protocol.serializer.javascript.GenerateJsUtils;
|
||||
import com.zfoo.protocol.serializer.lua.GenerateLuaUtils;
|
||||
import com.zfoo.protocol.serializer.python.GeneratePyUtils;
|
||||
import com.zfoo.protocol.serializer.typescript.GenerateTsUtils;
|
||||
import com.zfoo.protocol.util.FileUtils;
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -61,6 +63,22 @@ public abstract class GenerateProtocolFile {
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给每行新增若干Tab
|
||||
*/
|
||||
public static String addTabs(String str, int deep) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return str;
|
||||
}
|
||||
var splits = str.split(FileUtils.LS_REGEX);
|
||||
var builder = new StringBuilder();
|
||||
for (var split : splits) {
|
||||
builder.append(TAB.repeat(Math.max(0, deep)));
|
||||
builder.append(split).append(FileUtils.LS);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
generateProtocolFilter = null;
|
||||
index = null;
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.zfoo.protocol.anno.Compatible;
|
||||
import com.zfoo.protocol.anno.Note;
|
||||
import com.zfoo.protocol.anno.Protocol;
|
||||
import com.zfoo.protocol.collection.CollectionUtils;
|
||||
import com.zfoo.protocol.generate.GenerateProtocolFile;
|
||||
import com.zfoo.protocol.model.Pair;
|
||||
import com.zfoo.protocol.serializer.protobuf.parser.Proto;
|
||||
import com.zfoo.protocol.serializer.protobuf.parser.ProtoParser;
|
||||
@@ -117,10 +118,40 @@ public abstract class GeneratePbUtils {
|
||||
if (CollectionUtils.isEmpty(pbMessages)) {
|
||||
continue;
|
||||
}
|
||||
for (var pbMessage : pbMessages) {
|
||||
var code = buildMessage(pbGenerateOperation, protos, proto, pbMessage);
|
||||
var filePath = StringUtils.format("{}/{}/{}.java", messageOutputPath, proto.getName(), pbMessage.getName());
|
||||
FileUtils.writeStringToFile(new File(filePath), code, false);
|
||||
if (pbGenerateOperation.isOneProtocol()) {
|
||||
var builder = new StringBuilder();
|
||||
var outClassName = toOutClassName(proto);
|
||||
// import other class
|
||||
var imports = buildOneProtocolMessageImports(pbGenerateOperation, protos, proto);
|
||||
builder.append(imports);
|
||||
// out class builder
|
||||
var protoComment = buildProtoComment(proto);
|
||||
builder.append(protoComment);
|
||||
builder.append(StringUtils.format("public class {} {", outClassName)).append(LS);
|
||||
// inner class builder
|
||||
for (var pbMessage : pbMessages) {
|
||||
// document
|
||||
var documentComment = buildDocumentComment(pbMessage);
|
||||
builder.append(GenerateProtocolFile.addTabs(documentComment, 1));
|
||||
// message
|
||||
if (pbGenerateOperation.isRecordClass()) {
|
||||
var recordBody = buildRecordBody(pbMessage);
|
||||
builder.append(GenerateProtocolFile.addTabs(recordBody, 1));
|
||||
} else {
|
||||
var classBody = buildClassBody(pbMessage);
|
||||
classBody = classBody.replaceFirst("public class ", "public static class ");
|
||||
builder.append(GenerateProtocolFile.addTabs(classBody, 1));
|
||||
}
|
||||
}
|
||||
builder.append("}");
|
||||
var filePath = StringUtils.format("{}/{}.java", messageOutputPath, outClassName);
|
||||
FileUtils.writeStringToFile(new File(filePath), builder.toString(), false);
|
||||
} else {
|
||||
for (var pbMessage : pbMessages) {
|
||||
var code = buildMessage(pbGenerateOperation, protos, proto, pbMessage);
|
||||
var filePath = StringUtils.format("{}/{}/{}.java", messageOutputPath, proto.getName(), pbMessage.getName());
|
||||
FileUtils.writeStringToFile(new File(filePath), code, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,6 +196,10 @@ public abstract class GeneratePbUtils {
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
private static boolean isCompatiblePbField(PbField pbField) {
|
||||
return pbField.getTag() >= COMPATIBLE_FIELD_TAG;
|
||||
}
|
||||
|
||||
public static String buildMessage(PbGenerateOperation pbGenerateOperation, List<Proto> protos, Proto proto, PbMessage pbMessage) {
|
||||
var builder = new StringBuilder();
|
||||
|
||||
@@ -202,7 +237,7 @@ public abstract class GeneratePbUtils {
|
||||
imports.add(Note.class.getName());
|
||||
}
|
||||
|
||||
if (pbField.getTag() >= COMPATIBLE_FIELD_TAG) {
|
||||
if (isCompatiblePbField(pbField)) {
|
||||
imports.add(Compatible.class.getName());
|
||||
}
|
||||
|
||||
@@ -257,6 +292,16 @@ public abstract class GeneratePbUtils {
|
||||
throw new RuntimeException(StringUtils.format("not found type:[{}] in proto:[{}]", fieldType, proto.getName()));
|
||||
}
|
||||
|
||||
private static String buildProtoComment(Proto proto) {
|
||||
if (CollectionUtils.isEmpty(proto.getComments())) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
var builder = new StringBuilder();
|
||||
builder.append("/**").append(LS);
|
||||
proto.getComments().forEach(it -> builder.append(StringUtils.format(" * {}", it)).append(LS));
|
||||
builder.append(" */").append(LS);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String buildDocumentComment(PbMessage msg) {
|
||||
if (CollectionUtils.isEmpty(msg.getComments())) {
|
||||
@@ -311,7 +356,7 @@ public abstract class GeneratePbUtils {
|
||||
|
||||
var fieldComment = buildFieldComment(pbField);
|
||||
builder.append(fieldComment);
|
||||
if (pbField.getTag() >= COMPATIBLE_FIELD_TAG) {
|
||||
if (isCompatiblePbField(pbField)) {
|
||||
var tag = pbField.getTag() - COMPATIBLE_FIELD_TAG;
|
||||
builder.append(TAB).append(StringUtils.format("@Compatible({})", tag)).append(LS);
|
||||
}
|
||||
@@ -343,7 +388,7 @@ public abstract class GeneratePbUtils {
|
||||
|
||||
var fieldComment = buildFieldComment(pbField);
|
||||
builder.append(fieldComment);
|
||||
if (pbField.getTag() >= COMPATIBLE_FIELD_TAG) {
|
||||
if (isCompatiblePbField(pbField)) {
|
||||
var tag = pbField.getTag() - COMPATIBLE_FIELD_TAG;
|
||||
builder.append(TAB).append(StringUtils.format("@Compatible({})", tag)).append(LS);
|
||||
}
|
||||
@@ -370,4 +415,96 @@ public abstract class GeneratePbUtils {
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
private static String toOutClassName(Proto proto) {
|
||||
var protoName = proto.getName();
|
||||
var splits = protoName.split("[-_]");
|
||||
var builder = new StringBuilder();
|
||||
for (var split : splits) {
|
||||
if (StringUtils.isBlank(split)) {
|
||||
continue;
|
||||
}
|
||||
// 首字母大写
|
||||
builder.append(StringUtils.capitalize(split.trim()));
|
||||
}
|
||||
var outClassName = builder.toString();
|
||||
for (var pbMessage : proto.getPbMessages()) {
|
||||
if (pbMessage.getName().equals(outClassName)) {
|
||||
builder.append("s");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String buildOneProtocolMessageImports(PbGenerateOperation pbGenerateOperation, List<Proto> protos, Proto proto) {
|
||||
var imports = new HashSet<String>();
|
||||
imports.add(Protocol.class.getName());
|
||||
|
||||
for (var pbMessage : proto.getPbMessages()) {
|
||||
var pbFields = pbMessage.getFields();
|
||||
if (CollectionUtils.isEmpty(pbFields)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
for (var pbField : pbFields) {
|
||||
if (CollectionUtils.isNotEmpty(pbField.getComments())) {
|
||||
imports.add(Note.class.getName());
|
||||
}
|
||||
|
||||
if (isCompatiblePbField(pbField)) {
|
||||
imports.add(Compatible.class.getName());
|
||||
}
|
||||
|
||||
if (pbField instanceof PbMapField) {
|
||||
imports.add(Map.class.getName());
|
||||
var pbMapField = (PbMapField) pbField;
|
||||
buildOneProtocolImports(pbGenerateOperation, protos, proto, pbMapField.getKey().value(), imports);
|
||||
buildOneProtocolImports(pbGenerateOperation, protos, proto, pbMapField.getValue(), imports);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pbField.getCardinality() == PbField.Cardinality.REPEATED) {
|
||||
imports.add(List.class.getName());
|
||||
}
|
||||
|
||||
buildOneProtocolImports(pbGenerateOperation, protos, proto, pbField.getType(), imports);
|
||||
}
|
||||
}
|
||||
|
||||
var builder = new StringBuilder();
|
||||
imports.stream()
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.forEach(it -> builder.append(StringUtils.format("import {};", it)).append(LS));
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static void buildOneProtocolImports(PbGenerateOperation pbGenerateOperation, List<Proto> protos, Proto proto, String fieldType, Set<String> imports) {
|
||||
// 基本数据类型不需要导入
|
||||
var typeProtobuf = PbType.typeOfProtobuf(fieldType);
|
||||
if (typeProtobuf != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 属于同一个包不需要导入
|
||||
if (proto.getPbMessages().stream().anyMatch(it -> it.getName().equals(fieldType))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历其它的proto找到需要导入的类
|
||||
for (var pt : protos) {
|
||||
for (var msg : pt.getPbMessages()) {
|
||||
if (msg.getName().equals(fieldType)) {
|
||||
if (StringUtils.isBlank(pbGenerateOperation.getJavaPackage())) {
|
||||
imports.add(StringUtils.format("static {}.*", toOutClassName(pt)));
|
||||
} else {
|
||||
imports.add(StringUtils.format("static {}.{}.*", pbGenerateOperation.getJavaPackage(), toOutClassName(pt)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException(StringUtils.format("not found type:[{}] in proto:[{}]", fieldType, proto.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -21,7 +21,11 @@ public class PbGenerateOperation {
|
||||
/**
|
||||
* Whether generated class is record
|
||||
*/
|
||||
private boolean recordClass = false;
|
||||
private boolean recordClass;
|
||||
/**
|
||||
* All protocol files are generated in a single protocol file.
|
||||
*/
|
||||
private boolean oneProtocol;
|
||||
|
||||
public String getProtoPath() {
|
||||
return protoPath;
|
||||
@@ -54,4 +58,12 @@ public class PbGenerateOperation {
|
||||
public void setRecordClass(boolean recordClass) {
|
||||
this.recordClass = recordClass;
|
||||
}
|
||||
|
||||
public boolean isOneProtocol() {
|
||||
return oneProtocol;
|
||||
}
|
||||
|
||||
public void setOneProtocol(boolean oneProtocol) {
|
||||
this.oneProtocol = oneProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ public abstract class FileUtils {
|
||||
* </pre>
|
||||
*/
|
||||
public static final String LS = System.lineSeparator();
|
||||
public static final String LS_REGEX = "\\r?\\n";
|
||||
public static final String UNIX_LS = "\\n";
|
||||
public static final String WINDOWS_LS = "\\r\\n";
|
||||
// The file copy buffer size (30 MB)
|
||||
|
||||
@@ -28,7 +28,7 @@ public class GenerateProtobufTesting {
|
||||
buildOption.setOutputPath("D:\\github\\zfoo\\protocol\\src\\test\\zfoopb");
|
||||
buildOption.setJavaPackage("com.zfoo.protocol.generate.test");
|
||||
// buildOption.setRecordClass(true);
|
||||
|
||||
// buildOption.setOneProtocol(true);
|
||||
GeneratePbUtils.create(buildOption);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
syntax = "proto3";
|
||||
// start_protocol_id = 100
|
||||
|
||||
package test.message;
|
||||
|
||||
// 这个是整个proto的注释
|
||||
import "one_map.proto";
|
||||
import "one_message.proto";
|
||||
option java_package = "start";
|
||||
|
||||
// start_protocol_id = 100
|
||||
|
||||
|
||||
// 如果protobuf的字段的tag超过1000,则视这个字段为需要兼容的协议字段
|
||||
message AllType {
|
||||
|
||||
Reference in New Issue
Block a user