feat[protobuf]: generate pojo file from proto of protobuf

This commit is contained in:
godotg committed 2023-12-02 21:05:59 +08:00
1 parent f6aa27cb1d
commit c1b859c8c3
28 files changed
+4348 -1

No files matched your search

@@ -0,0 +1,70 @@
/*
* 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;
import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.serializer.protobuf.codegen.IfaceGenerator;
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;
public class GeneratePbUtils {
public static void create(PbBuildOption buildOption) {
var protoPathFile = new File(buildOption.getProtoPath());
if (!protoPathFile.exists()) {
throw new RuntimeException(StringUtils.format("proto path:[{}] not exist", buildOption.getProtoPath()));
}
var protoFiles = FileUtils.getAllReadableFiles(protoPathFile)
.stream()
.filter(it -> it.getName().toLowerCase().endsWith(".proto"))
.toList();
if (CollectionUtils.isEmpty(protoFiles)) {
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();
}
public static List<Proto> parseProtoFile(List<File> protoFiles) {
var protos = new ArrayList<Proto>();
for (var protoFile : protoFiles) {
var strs = FileUtils.readFileToStringList(protoFile)
.stream()
.filter(StringUtils::isNotBlank)
.toArray();
var protoString = StringUtils.joinWith(FileUtils.LS, strs);
if (StringUtils.isBlank(protoString)) {
continue;
}
ProtoParser parser = new ProtoParser(protoString);
Proto proto = parser.parse();
proto.setFile(protoFile);
protos.add(proto);
}
return protos;
}
}
@@ -0,0 +1,47 @@
/*
* 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;
public class PbBuildOption {
private String protoPath;
private String outputPath;
private boolean isNested;
public String getProtoPath() {
return protoPath;
}
public void setProtoPath(String protoPath) {
this.protoPath = protoPath;
}
public String getOutputPath() {
return outputPath;
}
public void setOutputPath(String outputPath) {
this.outputPath = outputPath;
}
public boolean isIsNested() {
return isNested;
}
public PbBuildOption setIsNested(boolean isNested) {
this.isNested = isNested;
return this;
}
}
@@ -0,0 +1,407 @@
/*
* 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.builder;
import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.serializer.protobuf.PbBuildOption;
import com.zfoo.protocol.serializer.protobuf.internal.CodeBuilder;
import com.zfoo.protocol.serializer.protobuf.wire.*;
import com.zfoo.protocol.serializer.protobuf.wire.Field.Type;
import com.zfoo.protocol.serializer.protobuf.wire.ProtoEnum.EnumEntry;
import com.zfoo.protocol.serializer.protobuf.wire.WireFormat.JavaType;
import com.zfoo.protocol.serializer.protobuf.wire.parser.Proto;
import com.zfoo.protocol.util.StringUtils;
import java.util.*;
public class JavaBuilder {
private static final List<String> BASE_TYPES;
static {
BASE_TYPES = new ArrayList<>();
String[] types = ("double,float,int32,int64,uint32,uint64,sint32,sint64,"
+ "fixed32,fixed64,sfixed32,sfixed64,bool,string,bytes").split(",");
BASE_TYPES.addAll(Arrays.asList(types));
}
private String getOptionJavaType(List<Option> options) {
String jType = "";
if (options != null && !options.isEmpty()) {
for (Option option : options) {
if ("java_type".equals(option.getName())) {
jType = option.getValue();
}
}
}
return jType;
}
public String getJavaType(Field field, List<String> imps, PbBuildOption buildOps) {
String type = field.getTypeString();
if (field instanceof MapField) {
MapField mf = (MapField) field;
type = "Map<" + getJavaType(mf.getKey().value()) + ", "
+ getJavaType(mf.getValue()) + ">";
return type;
}
String jType = getOptionJavaType(field.getOptions());
if (!BASE_TYPES.contains(type.toLowerCase(Locale.ENGLISH))) {
return type;
}
JavaType javaType = null;
javaType = Type.valueOf(type.toUpperCase(Locale.ENGLISH)).javaType();
if (javaType != null && javaType.getTypeString() != null) {
String optionJavaType = getOptionJavaType(field.getOptions());
if (optionJavaType == null || optionJavaType.isEmpty()) {
type = javaType.getTypeString();
} else {
int lastDot = optionJavaType.lastIndexOf(".");
if (lastDot == -1) {
type = optionJavaType;
} else {
if (!imps.contains(optionJavaType)) {
imps.add(optionJavaType);
}
type = optionJavaType.substring(lastDot + 1);
}
}
}
if ("int64".equals(field.getType()) && jType != null) {
switch (jType) {
case "LocalDateTime":
addImport(imps, "java.time.LocalDateTime");
type = jType;
break;
case "LocalDate":
type = jType;
addImport(imps, "java.time.LocalDate");
break;
default:
break;
}
}
return type;
}
private void addImport(List<String> imps, String imp) {
if (imps == null) {
return;
}
if (!imps.contains(imp)) {
imps.add(imp);
}
}
public String getJavaType(String type) {
if (BASE_TYPES.contains(type.toLowerCase(Locale.ENGLISH))) {
return Type.valueOf(type.toUpperCase(Locale.ENGLISH)).javaType().getTypeString();
} else {
return type;
}
}
public String getAnnotationType(Field field, Proto proto, Map<String, ProtoEnum> protoEnums) {
String type = field.getTypeString();
if (BASE_TYPES.contains(type.toLowerCase(Locale.ENGLISH))) {
return "Type." + Type.valueOf(type.toUpperCase(Locale.ENGLISH));
} else {
String name = getJavaPackage(proto) + "." + type;
if (protoEnums.containsKey(name)) {
return "Type." + Type.ENUM;
} else {
return "Type." + Type.MESSAGE;
}
}
}
private void buildMsgImps(ProtoMessage msg, List<Field> tmp, List<String> imps,
PbBuildOption buildOps) {
if (msg.getFields() != null) {
msg.getFields().forEach(e -> {
getJavaType(e, imps, buildOps);
tmp.add(e);
});
}
List<Oneof> oneofs = msg.getOneofs();
if (oneofs != null) {
oneofs.forEach(o -> {
if (o.getFields() != null) {
o.getFields().forEach(tmp::add);
}
});
}
for (int i = 0; i < tmp.size(); i++) {
if (tmp.get(i) instanceof MapField) {
addImport(imps, "java.util.Map");
} else if (tmp.get(i).getCardinality() == Field.Cardinality.REPEATED) {
addImport(imps, "java.util.List");
addImport(imps, "java.util.ArrayList");
}
}
}
private void buildDocComment(CodeBuilder cb, Comment comment, int level) {
if (comment == null || comment.getLines() == null || comment.getLines().isEmpty()) {
return;
}
cb.t(level).c("/**").ln();
comment.getLines().forEach(c -> cb.t(level).c(" * ").c(c).ln());
cb.t(level).c(" */").ln();
}
private Map<String, ProtoEnum> getAllProtoEnum(Proto proto, Map<String, Proto> protos) {
Map<String, ProtoEnum> allEnums = new HashMap<>();
List<ProtoEnum> protoEnums = proto.getEnums();
if (protoEnums != null && !protoEnums.isEmpty()) {
for (ProtoEnum protoEnum : protoEnums) {
String packName = proto.getProtoPackage();
String javaPack = getJavaPackage(proto);
if (javaPack != null && javaPack.trim().length() > 0) {
packName = javaPack;
}
allEnums.put(packName + "." + protoEnum.getName(), protoEnum);
}
}
List<String> impProtos = proto.getImports();
if (impProtos == null || impProtos.isEmpty()) {
return allEnums;
}
for (String protoName : impProtos) {
Proto impProto = protos.get(protoName);
if (impProto == null) {
continue;
}
allEnums.putAll(getAllProtoEnum(impProto, protos));
}
return allEnums;
}
private String getJavaPackage(Proto proto) {
if (CollectionUtils.isEmpty(proto.getOptions())) {
return StringUtils.EMPTY;
}
for (Option option : proto.getOptions()) {
if ("java_package".equalsIgnoreCase(option.getName())) {
return option.getValue();
}
}
return StringUtils.EMPTY;
}
public String buildMessage(Proto proto, ProtoMessage msg, int indent, Map<String, String> defineMsgs, PbBuildOption buildOps, Map<String, Proto> protos) {
Map<String, ProtoEnum> protoEnums = getAllProtoEnum(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);
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());
}
cb.ln();
buildDocComment(cb, msg.getComment(), level - 1);
cb.t(level - 1).e("public class $name$ {").arg(msg.getName()).ln(2);
CodeBuilder getCode;
CodeBuilder setCode;
int size = fields.size();
List<String> getCodes = new ArrayList<>(size);
List<String> setCodes = new ArrayList<>(size);
CodeBuilder cons = new CodeBuilder();
for (int i = 0; i < size; i++) {
Field f = fields.get(i);
getCode = new CodeBuilder();
setCode = new CodeBuilder();
String typeName = getAnnotationType(f, proto, protoEnums);
if (msg.getName().equals("Type")) {
typeName = Proto.class.getPackage().getName() + ".wire.Field." + typeName;
}
buildDocComment(cb, f.getComment(), level);
String type = getJavaType(f, imps, buildOps);
String name = f.getName();
if (f.getCardinality() == Field.Cardinality.REPEATED) {
String boxedTypeName = getBoxedTypeName(f, buildOps);
type = "List<" + boxedTypeName + ">";
}
cb.t(level).e("private $type$ $name$;")
.arg(type, name).ln();
String getMethod;
if (!"bool".equalsIgnoreCase(f.getType())) {
getMethod = "get" +
f.getName().substring(0, 1).toUpperCase(Locale.ENGLISH) +
f.getName().substring(1);
} else {
getMethod = "is" +
f.getName().substring(0, 1).toUpperCase(Locale.ENGLISH) +
f.getName().substring(1);
}
getCode.t(level).e("public $type$ $getMethod$() {")
.arg(type, getMethod).ln();
getCode.t(level + 1).e("return $name$;").arg(f.getName()).ln();
getCode.t(level).c("}").ln();
getCodes.add(getCode.toString());
String retType = "void";
String setMethod = "set" +
f.getName().substring(0, 1).toUpperCase(Locale.ENGLISH) +
f.getName().substring(1);
setCode.t(level).e("public $retType$ $setMethod$($type$ $name$) {")
.arg(retType, setMethod, type, name).ln();
setCode.t(level + 1).e("this.$name$ = $name$;")
.arg(f.getName(), f.getName()).ln();
setCode.t(level).c("}").ln();
setCodes.add(setCode.toString());
}
cb.ln().c(cons.toString());
for (int i = 0; i < size; i++) {
cb.ln().c(getCodes.get(i));
cb.ln().c(setCodes.get(i));
}
nestMessageMessage(proto, cb, msg.getMessages(), defineMsgs, level, protos);
nestMessageEnum(cb, msg.getEnums(), level);
cb.t(level - 1).c("}");
return cb.toString();
}
private String getBoxedTypeName(Field f, PbBuildOption buildOps) {
String type = getJavaType(f, null, buildOps);
if (BASE_TYPES.contains(f.getTypeString())) {
JavaType javaType = null;
javaType = Type.valueOf(f.getTypeString().toUpperCase(Locale.ENGLISH)).javaType();
if (javaType != null && javaType.getTypeString() != null) {
type = javaType.getBoxedType();
}
}
return type;
}
private void nestMessageMessage(Proto proto, CodeBuilder cb,
List<ProtoMessage> msgs, Map<String, String> defineMsgs,
int level, Map<String, Proto> protos) {
if (msgs == null || msgs.isEmpty()) {
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)));
}
private void nestMessageEnum(CodeBuilder cb, List<ProtoEnum> enums, int indent) {
if (enums == null || enums.isEmpty()) {
return;
}
cb.ln();
PbBuildOption nestedOps = new PbBuildOption();
nestedOps.setIsNested(true);
enums.stream()
.sorted(Comparator.comparing(ProtoEnum::getName))
.forEach(e -> cb.c(buildEnum(e, indent + 1, nestedOps)).ln(2));
}
private String getFillSpaces(int count) {
if (count <= 0) {
return StringUtils.EMPTY;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(" ");
}
return sb.toString();
}
public String buildEnum(ProtoEnum protoEnum, final int indent, PbBuildOption buildOps) {
int level = (indent < 1) ? 1 : indent;
CodeBuilder cb = new CodeBuilder();
cb.t(level - 1).e("public enum $name$ {").arg(protoEnum.getName()).ln();
List<EnumEntry> entries = protoEnum.getEntries();
if (entries != null && !entries.isEmpty()) {
Optional<EnumEntry> maxLen = entries.stream()
.max(Comparator.comparingInt(e -> e.getLabel().length()));
int len = maxLen.isPresent() ? maxLen.get().getLabel().length() : 1;
List<EnumEntry> es = new ArrayList<>();
entries.stream()
.sorted(Comparator.comparingInt(EnumEntry::getValue))
.forEach(es::add);
CodeBuilder vsb = new CodeBuilder();
CodeBuilder psb = new CodeBuilder();
for (int i = 0; i < es.size(); i++) {
EnumEntry o = es.get(i);
String sep = (i != es.size() - 1) ? "," : ";";
String spaces = getFillSpaces(len - o.getLabel().length());
String[] args = new String[]{o.getLabel(), spaces,
String.valueOf(o.getValue()), sep};
cb.t(level).e("$label$$spaces$($value$)$;$").arg(args).ln();
psb.t(level).e("public static final int $lable$_VALUE = $value$;")
.arg(o.getLabel(), String.valueOf(o.getValue())).ln();
vsb.t(level + 2).e("case $value$:").arg(String.valueOf(o.getValue())).ln();
vsb.t(level + 3).e("return $name$.$label$;")
.arg(protoEnum.getName(), o.getLabel()).ln();
}
cb.ln();
cb.c(psb.toString()).ln();
cb.t(level).c("private final int value;").ln(2);
cb.t(level).e("private $label$(int value) {").arg(protoEnum.getName()).ln();
cb.t(level + 1).c("this.value = value;").ln();
cb.t(level).c("}").ln();
cb.t(level).c("public int getValue() {").ln();
cb.t(level + 1).c("return this.value;").ln();
cb.t(level).c("}").ln(2);
cb.t(level).e("public static $name$ valueOf(int value) {")
.arg(protoEnum.getName()).ln();
cb.t(level + 1).c("switch (value) {").ln();
cb.c(vsb.toString());
cb.t(level + 2).c("default:").ln();
cb.t(level + 3).e("throw new IllegalArgumentException("
+ "\"no enum value $value$ \" + value);")
.arg(protoEnum.getName()).ln();
cb.t(level + 1).c("}").ln();
cb.t(level).c("}").ln();
}
cb.t(level - 1).c("}");
return cb.toString();
}
}
@@ -0,0 +1,102 @@
/*
* 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.ProtoEnum;
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<ProtoEnum> enums = proto.getEnums();
if (CollectionUtils.isNotEmpty(enums)) {
String enumPath = srcPath + File.separator;
enums.stream().sorted(Comparator.comparing(ProtoEnum::getName))
.forEach(it -> {
var code = builder.buildEnum(it, 1, buildOps);
var filePath = enumPath + File.separator + it.getName() + ".java";
FileUtils.writeStringToFile(new File(filePath), code, false);
}
);
}
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);
});
}
}
}
@@ -0,0 +1,226 @@
/*
* 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.internal;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 简易的代码构建器,方便进行行内的占位符,占位符采用$key$的形式方便预生成代码的阅读.
* 占位符采用下标的方式赋值$name$中间的字符会被忽略
*/
public class CodeBuilder {
private final StringBuilder code;
private static final String INDENT_CHARS = " ";
private static final String SHORT_INDENT_CHARS = "\t";
private static final String LINE_SPERATOR = "\n";
private final BlockingQueue<String> prepares;
private final BlockingQueue<String> args;
/**
* 使用紧凑的缩进,紧凑缩进使用一个"\t"tab键缩进的方式
*/
private boolean compactIdentation;
public CodeBuilder() {
code = new StringBuilder();
prepares = new ArrayBlockingQueue<>(40);
args = new ArrayBlockingQueue<>(40);
compactIdentation = false;
}
/**
* 向现有代码中缩进指定的级别
*
* @param level 缩进的级别
* @return
*/
public CodeBuilder t(int level) {
flush();
for (int i = 0; i < level; i++) {
if (!isCompactIdentation()) {
code.append(INDENT_CHARS);
} else {
code.append(SHORT_INDENT_CHARS);
}
}
return this;
}
/**
* 为占位符的表达式中占位符进行赋值
*
* @param arg 占位符赋值的字符串数组
* @return
*/
public CodeBuilder arg(String... arg) {
if (prepares.isEmpty()) {
return this;
}
for (String a : arg) {
args.offer(a);
}
if (args.size() >= prepares.size() - 1) {
flush();
}
return this;
}
/**
* 向代码中追加字符串
*
* @param str 需要追加的字符串
* @return
*/
public CodeBuilder c(String str) {
flush();
code.append(str);
return this;
}
/**
* 指定需要替换占位符的字符串,占位符使用"$$"包含,"$"内部的字符不会输出一个"$$"作为一个
* 占位符,如果需要输出"$"则使用"\"进行转义
*
* @param expression 需要替换占位符的表达式
* @return
*/
public CodeBuilder e(String expression) {
if (expression == null || expression.isEmpty()) {
return this;
}
flush();
char c;
boolean isStarted = false;
StringBuilder tmp = new StringBuilder();
for (int i = 0; i < expression.length(); i++) {
c = expression.charAt(i);
if (c == '$') {
isStarted = dealTmpData(tmp, c, isStarted);
} else {
tmp.append(c);
}
}
prepares.offer(tmp.toString());
return this;
}
/**
* 当出现$占位符时,判断解析状态并返回下一个字符的解析状态
*
* @param tmp 临时保存的数据
* @param c 当前的字符
* @param isStarted 当前解析的状态
* @return
*/
private boolean dealTmpData(StringBuilder tmp, char c, final boolean isStarted) {
boolean start = isStarted;
if (tmp.length() == 0) {
if (isStarted) {
start = false;
tmp.delete(0, tmp.length());
} else {
prepares.offer(tmp.toString());
start = true;
tmp.delete(0, tmp.length());
}
} else {
if (tmp.charAt(tmp.length() - 1) != '\\') {
if (isStarted) {
start = false;
tmp.delete(0, tmp.length());
} else {
prepares.offer(tmp.toString());
start = true;
tmp.delete(0, tmp.length());
}
} else {
tmp.setCharAt(tmp.length() - 1, c);
}
}
return start;
}
@Override
public String toString() {
flush();
return code.toString();
}
/**
* 刷新占位符替换后的字符串到现有代码中,当给定的变量个数多余占位符的个数时多余的变量
* 不会被输出
*
* @return
*/
public CodeBuilder flush() {
if (prepares.isEmpty()) {
args.clear();
return this;
}
int count = prepares.size();
String str;
for (int i = 0; i < count - 1; i++) {
str = prepares.poll();
if (str != null) {
code.append(str);
}
String val = args.poll();
if (val != null) {
code.append(val);
}
}
str = prepares.poll();
while (str != null) {
code.append(str);
str = prepares.poll();
}
args.clear();
return this;
}
/**
* 向代码中追加指定个数的换行符
*
* @param rows 换行符的个数
* @return
*/
public CodeBuilder ln(int rows) {
flush();
code.append(LINE_SPERATOR);
if (rows > 1) {
for (int i = 1; i < rows; i++) {
code.append(LINE_SPERATOR);
}
}
return this;
}
/**
* 向代码中追加一个换行符,如果有没有输出的占位符的字符串则会先追加占位符的字符串
*
* @return
*/
public CodeBuilder ln() {
return ln(1);
}
/**
* 使用紧凑的缩进,紧凑缩进使用一个"\t"tab键缩进的方式
*/
public boolean isCompactIdentation() {
return compactIdentation;
}
}
@@ -0,0 +1,80 @@
/*
* 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.wire;
import java.util.ArrayList;
import java.util.List;
/**
* proto文件中注释的结构定义
*/
public class Comment {
public enum CommentType {
/**
* 以"//"开头的注释类型
*/
INLINE,
/**
* 以 "/*"开头的注释类型
*/
MULTILINE,
/**
* 以 "/**"开头的注释类型
*/
DOCUMENT
}
/**
* 注释内容文本行列表
*/
private List<String> lines;
/**
* 注释的类型
*/
private CommentType type = CommentType.INLINE;
/**
* 注释内容文本行列表
*/
public List<String> getLines() {
if (lines == null) {
lines = new ArrayList<>();
}
return lines;
}
public void setLines(List<String> lines) {
if (lines instanceof ArrayList) {
this.lines = lines;
} else {
getLines();
this.lines.addAll(lines);
}
}
/**
* 注释的类型
*/
public CommentType getType() {
return type;
}
public void setType(CommentType type) {
this.type = type;
}
}
@@ -0,0 +1,67 @@
/*
* 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.wire;
import java.util.ArrayList;
import java.util.List;
/**
* protocol buffer扩展信息的定义
*/
public class Extend {
private Comment comment;
private String name;
private List<Field> fields;
public Extend setName(String name) {
this.name = name;
return this;
}
public String getName() {
return name;
}
public Extend setComment(Comment comment) {
this.comment = comment;
return this;
}
public Comment getComment() {
return comment;
}
public Extend addField(Field field) {
if (field == null) {
return this;
}
getFields().add(field);
return this;
}
public List<Field> getFields() {
if (fields == null) {
fields = new ArrayList<>();
}
return fields;
}
public void setFields(List<Field> fields) {
if (fields instanceof ArrayList) {
this.fields = fields;
} else {
getFields().addAll(fields);
}
}
}
@@ -0,0 +1,84 @@
/*
* 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.wire;
import com.zfoo.protocol.serializer.protobuf.wire.parser.ProtoParser;
/**
* protocol buffer 扩展tag的结构定义
*/
public class Extensions {
/**
* 扩展开始的Tag编号
*/
private int startTag;
/**
* 扩展结束的Tag编号
*/
private int endTag;
public Extensions setEndTag(int endTag) {
this.endTag = endTag;
return this;
}
public int getEndTag() {
return endTag;
}
public Extensions setStartTag(int startTag) {
this.startTag = startTag;
return this;
}
public int getStartTag() {
return startTag;
}
public static Extensions parseExtensions(String expression) {
String exp = expression.trim();
if (exp.isEmpty()) {
throw new RuntimeException("extensions expression is empty");
}
int index = exp.indexOf("to");
if (index < 0) {
throw new RuntimeException("extensions expression must be start to end");
}
String sstart = exp.substring(0, index).trim();
if (sstart.isEmpty()) {
throw new RuntimeException("extensions expression start empty");
}
int start = ProtoParser.parseInt(sstart);
if (start < 1) {
throw new RuntimeException("extensions expression start cann't be " + start);
}
String sEnd = exp.substring(index + 2).trim();
if (sEnd.isEmpty()) {
throw new RuntimeException("extensions expression end empty");
}
int end;
if ("max".equalsIgnoreCase(sEnd)) {
end = WireFormat.MAX_TAG_VALUE;
} else {
end = ProtoParser.parseInt(sEnd);
}
if (end <= start) {
throw new RuntimeException("extensions start tag <= end tag");
}
Extensions ext = new Extensions();
ext.setStartTag(start).setEndTag(end);
return ext;
}
}
@@ -0,0 +1,365 @@
/*
* 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.wire;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* protocol buffer协议消息体属性数据类型定义
*/
public class Field {
/**
* 属性是否赋值类型
*/
public enum Cardinality {
/**
* 未知类型
*/
UNKNOWN("unknown"),
/**
* 必须赋值
*/
REQUIRED("required"),
/**
* 可以不赋值
*/
OPTIONAL("optional"),
/**
* 可以赋多个值
*/
REPEATED("repeated");
private final String value;
public String getValue() {
return this.value;
}
Cardinality(String value) {
this.value = value;
}
}
public enum Type {
FLOAT("float", WireFormat.JavaType.FLOAT, WireType.FIXED32),
DOUBLE("double", WireFormat.JavaType.DOUBLE, WireType.FIXED64),
INT32("int32", WireFormat.JavaType.INT, WireType.VARINT),
INT64("int64", WireFormat.JavaType.LONG, WireType.VARINT),
UINT32("uint32", WireFormat.JavaType.INT, WireType.VARINT),
UINT64("uint64", WireFormat.JavaType.LONG, WireType.VARINT),
SINT32("sint32", WireFormat.JavaType.INT, WireType.VARINT),
SINT64("sint64", WireFormat.JavaType.LONG, WireType.VARINT),
FIXED32("fixed32", WireFormat.JavaType.INT, WireType.FIXED32),
FIXED64("fixed64", WireFormat.JavaType.LONG, WireType.FIXED64),
SFIXED32("sfixed32", WireFormat.JavaType.INT, WireType.FIXED32),
SFIXED64("sfixed64", WireFormat.JavaType.LONG, WireType.FIXED64),
BOOL("bool", WireFormat.JavaType.BOOLEAN, WireType.VARINT),
ENUM("enum", WireFormat.JavaType.ENUM, WireType.VARINT),
STRING("string", WireFormat.JavaType.STRING, WireType.LENGTH_DELIMITED) {
@Override
public boolean packable() {
return false;
}
},
BYTES("bytes", WireFormat.JavaType.BYTES, WireType.LENGTH_DELIMITED) {
@Override
public boolean packable() {
return false;
}
},
MESSAGE("", WireFormat.JavaType.MESSAGE, WireType.LENGTH_DELIMITED) {
@Override
public boolean packable() {
return false;
}
},
OBJECT("OBJECT", WireFormat.JavaType.OBJECT, WireType.LENGTH_DELIMITED) {
@Override
public boolean packable() {
return false;
}
},
GROUP("group", WireFormat.JavaType.MESSAGE, WireType.START_GROUP) {
@Override
public boolean packable() {
return false;
}
},
MAP("", WireFormat.JavaType.MAP, WireType.LENGTH_DELIMITED) {
@Override
public boolean packable() {
return false;
}
};
private final String value;
private final WireFormat.JavaType javaType;
private final WireType wireType;
Type(String value, WireFormat.JavaType javaType, WireType wireType) {
this.value = value;
this.javaType = javaType;
this.wireType = wireType;
}
public WireFormat.JavaType javaType() {
return javaType;
}
public WireType wireType() {
return wireType;
}
public String value() {
return this.value;
}
public boolean packable() {
return true;
}
}
/**
* 消息属性的规则
*/
private Cardinality cardinality;
/**
* 消息属性名称
*/
private String name;
/**
* 消息属性值的类型
*/
private String type;
/**
* 消息属性值类型的字符串
*/
protected String typeString;
/**
* 属性的顺序标记一个消息内不允许重复
*/
private int tag;
/**
* 消息属性默认值
*/
private String defaultValue;
/**
* repeated的属性是否对包含的对象列表打包
*/
private List<Option> options;
/**
* 属性是否已经被标示为过时的
*/
private Boolean deprecated;
/**
* 消息属性的注释信息
*/
private Comment comment;
public String getTypeString() {
return this.getType();
}
/**
* 消息属性的规则
*
* @return the cardinality
*/
public Cardinality getCardinality() {
return cardinality;
}
/**
* 消息属性的规则
*
* @param cardinality the cardinality to set
* @return
*/
public Field setCardinality(Cardinality cardinality) {
this.cardinality = cardinality;
return this;
}
/**
* 消息属性名称
*
* @return the name
*/
public String getName() {
return name;
}
/**
* 消息属性名称
*
* @param name the name to set
* @return
*/
public Field setName(String name) {
this.name = name;
return this;
}
/**
* 消息属性值的类型
*
* @return the type
*/
public String getType() {
return type;
}
/**
* 消息属性值的类型
*
* @param type the type to set
* @return
*/
public Field setType(String type) {
this.type = type;
return this;
}
/**
* 属性的顺序标记一个消息内不允许重复
*
* @return the tag
*/
public int getTag() {
return tag;
}
/**
* 属性的顺序标记一个消息内不允许重复
*
* @param tag the tag to set
* @return
*/
public Field setTag(int tag) {
this.tag = tag;
return this;
}
/**
* 消息属性默认值
*
* @return the defaultValue
*/
public String getDefaultValue() {
if (defaultValue == null) {
try {
Type fieldType = Type.valueOf(type.toUpperCase(Locale.ENGLISH));
if (fieldType.javaType() != null && fieldType.javaType().defaultValue() != null) {
return String.valueOf(fieldType.javaType().defaultValue());
}
} catch (Exception e) {
}
} else {
return defaultValue;
}
return null;
}
/**
* 消息属性默认值
*
* @param defaultValue the defaultValue to set
* @return
*/
public Field setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
return this;
}
/**
* repeated的属性是否对包含的对象列表打包
*
* @return the options
*/
public List<Option> getOptions() {
if (options == null) {
options = new ArrayList<>();
}
return options;
}
/**
* repeated的属性是否对包含的对象列表打包
*
* @param options the options to set
* @return
*/
public Field setOptions(List<Option> options) {
if (options instanceof ArrayList) {
this.options = options;
} else {
getOptions().addAll(options);
}
return this;
}
/**
* 属性是否已经被标示为过时的
*
* @return the deprecated
*/
public Boolean getDeprecated() {
if (deprecated == null) {
for (Option option : getOptions()) {
if ("deprecated".equals(option.getName())) {
return Option.getBoolean(option.getValue());
}
}
return false;
} else {
return deprecated;
}
}
/**
* 属性是否已经被标示为过时的
*
* @param deprecated the deprecated to set
* @return
*/
public Field setDeprecated(Boolean deprecated) {
this.deprecated = deprecated;
return this;
}
/**
* 消息属性的注释信息
*
* @return the comment
*/
public Comment getComment() {
return comment;
}
/**
* 消息属性的注释信息
*
* @param comment the comment to set
* @return
*/
public Field setComment(Comment comment) {
this.comment = comment;
return this;
}
}
@@ -0,0 +1,67 @@
/*
* 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.wire;
/**
* protocol buffer中Map的Field的结构体定义
*/
public class MapField extends Field {
private Type key;
private String value;
@Override
public String getTypeString() {
StringBuilder sb = new StringBuilder();
String keyStr = "";
if (getKey() != null) {
keyStr = getKey().value();
}
sb.append("map<").append(keyStr).append(", ");
sb.append(getValue()).append(">");
return sb.toString();
}
/**
* @return the key
*/
public Type getKey() {
return key;
}
/**
* @param key the key to set
* @return
*/
public MapField setKey(Type key) {
this.key = key;
return this;
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @param value the value to set
* @return
*/
public MapField setValue(String value) {
this.value = value;
return this;
}
}
@@ -0,0 +1,36 @@
/*
* 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.wire;
import java.util.List;
/**
* 按名称预留的结构定义
*/
public class NameReserved extends Reserved {
/**
* 消息保留的属性名称列表
*/
private List<String> fieldNames;
public List<String> getFieldNames() {
return fieldNames;
}
public NameReserved setFieldNames(List<String> names) {
this.fieldNames = names;
return this;
}
}
@@ -0,0 +1,78 @@
/*
* 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.wire;
import java.util.ArrayList;
import java.util.List;
/**
* Oneof结构体定义,oneof定义几个属性必须有一个赋值的功能
*/
public class Oneof {
/**
* Oneof标示的名称
*/
private String name;
/**
* Oneof包含的属性列表
*/
private List<Field> fields;
/**
* Oneof的注释
*/
private Comment comment;
public Oneof setName(String name) {
this.name = name;
return this;
}
public String getName() {
return name;
}
public Oneof setComment(Comment comment) {
this.comment = comment;
return this;
}
public Comment getComment() {
return comment;
}
public void addField(Field field) {
if (getFields() == null) {
setFields(new ArrayList<>());
}
getFields().add(field);
}
/**
* Oneof包含的属性列表
*
* @return the fields
*/
public List<Field> getFields() {
return fields;
}
/**
* Oneof包含的属性列表
*
* @param fields the fields to set
*/
public void setFields(List<Field> fields) {
this.fields = fields;
}
}
@@ -0,0 +1,54 @@
/*
* 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.wire;
/**
* protocol buffer协议的Option的数据类型定义
*/
public class Option {
/**
* 选项名称
*/
private String name;
/**
* 选项对应的值
*/
private String value;
public Option setValue(String value) {
this.value = value;
return this;
}
public String getValue() {
return value;
}
public Option setName(String name) {
this.name = name;
return this;
}
public String getName() {
return name;
}
public static boolean getBoolean(String value) {
if (value == null || value.isEmpty()) {
return false;
}
return "true".equalsIgnoreCase(value) || "t".equalsIgnoreCase(value);
}
}
@@ -0,0 +1,196 @@
/*
* 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.wire;
import java.util.List;
/**
* protocol buffer的枚举类型的定义
*/
public class ProtoEnum {
/**
* 枚举名称
*/
private String name;
/**
* 枚举包含的值的定义
*/
private List<EnumEntry> entries;
/**
* 枚举包含的选项列表
*/
private List<Option> options;
/**
* 枚举项的定义
*/
public static class EnumEntry {
/**
* 枚举值定义的名称
*/
private String label;
/**
* 枚举定义的值
*/
private int value;
/**
* 枚举的选项列表
*/
private List<Option> options;
/**
* 枚举值的注释
*/
private Comment comment;
/**
* 枚举值定义的名称
*
* @return the label
*/
public String getLabel() {
return label;
}
/**
* 枚举值定义的名称
*
* @param label the label to set
* @return
*/
public EnumEntry setLabel(String label) {
this.label = label;
return this;
}
/**
* 枚举定义的值
*
* @return the value
*/
public int getValue() {
return value;
}
/**
* 枚举定义的值
*
* @param value the value to set
* @return
*/
public EnumEntry setValue(int value) {
this.value = value;
return this;
}
/**
* 枚举的选项列表
*
* @return the options
*/
public List<Option> getOptions() {
return options;
}
/**
* 枚举的选项列表
*
* @param options the options to set
* @return
*/
public EnumEntry setOptions(List<Option> options) {
this.options = options;
return this;
}
/**
* 枚举值的注释
*
* @return the comment
*/
public Comment getComment() {
return comment;
}
/**
* 枚举值的注释
*
* @param comment the comment to set
* @return
*/
public EnumEntry setComment(Comment comment) {
this.comment = comment;
return this;
}
}
/**
* 枚举名称
*
* @return the name
*/
public String getName() {
return name;
}
/**
* 枚举名称
*
* @param name the name to set
* @return
*/
public ProtoEnum setName(String name) {
this.name = name;
return this;
}
/**
* 枚举包含的值的定义
*
* @return the entries
*/
public List<EnumEntry> getEntries() {
return entries;
}
/**
* 枚举包含的值的定义
*
* @param entries the entries to set
* @return
*/
public ProtoEnum setEntries(List<EnumEntry> entries) {
this.entries = entries;
return this;
}
/**
* 枚举包含的选项列表
*
* @return the options
*/
public List<Option> getOptions() {
return options;
}
/**
* 枚举包含的选项列表
*
* @param options the options to set
* @return
*/
public ProtoEnum setOptions(List<Option> options) {
this.options = options;
return this;
}
}
@@ -0,0 +1,336 @@
/*
* 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.wire;
import java.util.ArrayList;
import java.util.List;
/**
* protocol buffer消息体的结构定义
*/
public class ProtoMessage {
/**
* 消息的名称
*/
private String name;
/**
* 消息包含的Fields列表
*/
private List<Field> fields;
/**
* 消息内嵌的Message列表
*/
private List<ProtoMessage> protoMessages;
/**
* 保留字段的设置
*/
private List<Reserved> reserveds;
/**
* 消息内嵌的扩展的消息列表
*/
private List<Extend> protoExtends;
/**
* 消息包含的扩展的列表
*/
private List<Extensions> extensionses;
/**
* 消息内置的枚举类型
*/
private List<ProtoEnum> enums;
/**
* 消息包含的Oneof的列表
*/
private List<Oneof> oneofs;
/**
* 消息的备注信息
*/
private Comment comment;
public ProtoMessage addMessage(ProtoMessage child) {
if (child == null) {
return this;
}
getMessages().add(child);
return this;
}
public ProtoMessage addField(Field field) {
if (field == null) {
return this;
}
getFields().add(field);
return this;
}
public ProtoMessage addEnum(ProtoEnum protoEnum) {
if (protoEnum == null) {
return this;
}
getEnums().add(protoEnum);
return this;
}
public ProtoMessage addExtensions(Extensions extensions) {
if (extensions == null) {
return this;
}
getExtensionses().add(extensions);
return this;
}
public ProtoMessage addReserved(Reserved reserved) {
if (reserved == null) {
return this;
}
getReserveds().add(reserved);
return this;
}
public ProtoMessage addOneof(Oneof oneof) {
if (oneof == null) {
return this;
}
getOneofs().add(oneof);
return this;
}
/**
* 消息的名称
*
* @return the name
*/
public String getName() {
return name;
}
/**
* 消息的名称
*
* @param name the name to set
*/
public ProtoMessage setName(String name) {
this.name = name;
return this;
}
/**
* 消息包含的Fields列表
*
* @return the fields
*/
public List<Field> getFields() {
if (fields == null) {
fields = new ArrayList<>();
}
return fields;
}
/**
* 消息包含的Fields列表
*
* @param fields the fields to set
*/
public ProtoMessage setFields(List<Field> fields) {
if (fields instanceof ArrayList) {
this.fields = fields;
} else {
this.fields = new ArrayList<>();
this.fields.addAll(fields);
}
return this;
}
/**
* 消息内嵌的Message列表
*
* @return the messages
*/
public List<ProtoMessage> getMessages() {
if (protoMessages == null) {
protoMessages = new ArrayList<>();
}
return protoMessages;
}
/**
* 消息内嵌的Message列表
*
* @param protoMessages the messages to set
*/
public ProtoMessage setMessages(List<ProtoMessage> protoMessages) {
if (protoMessages instanceof ArrayList) {
this.protoMessages = protoMessages;
} else {
this.protoMessages = new ArrayList<>();
this.protoMessages.addAll(protoMessages);
}
return this;
}
/**
* 保留字段的设置
*
* @return the reserveds
*/
public List<Reserved> getReserveds() {
if (reserveds == null) {
reserveds = new ArrayList<>();
}
return reserveds;
}
/**
* 保留字段的设置
*
* @param reserveds the reserveds to set
*/
public ProtoMessage setReserveds(List<Reserved> reserveds) {
if (reserveds instanceof ArrayList) {
this.reserveds = reserveds;
} else {
this.reserveds = new ArrayList<>();
this.reserveds.addAll(reserveds);
}
return this;
}
/**
* 消息内嵌的扩展的消息列表
*
* @return the protoExtends
*/
public List<Extend> getProtoExtends() {
if (protoExtends == null) {
protoExtends = new ArrayList<>();
}
return protoExtends;
}
public ProtoMessage addProtoExtends(Extend extend) {
if (extend == null) {
return this;
}
getProtoExtends().add(extend);
return this;
}
/**
* 消息内嵌的扩展的消息列表
*
* @param protoExtends the protoExtends to set
*/
public ProtoMessage setProtoExtends(List<Extend> protoExtends) {
this.protoExtends = protoExtends;
return this;
}
/**
* 消息包含的扩展的列表
*
* @return the extensionses
*/
public List<Extensions> getExtensionses() {
if (extensionses == null) {
extensionses = new ArrayList<>();
}
return extensionses;
}
/**
* 消息包含的扩展的列表
*
* @param extensionses the extensionses to set
*/
public ProtoMessage setExtensionses(List<Extensions> extensionses) {
if (extensionses instanceof ArrayList) {
this.extensionses = extensionses;
} else {
this.extensionses = new ArrayList<>();
this.extensionses.addAll(extensionses);
}
return this;
}
/**
* 消息内置的枚举类型
*
* @return the enums
*/
public List<ProtoEnum> getEnums() {
if (enums == null) {
enums = new ArrayList<>();
}
return enums;
}
/**
* 消息内置的枚举类型
*
* @param enums the enums to set
*/
public ProtoMessage setEnums(List<ProtoEnum> enums) {
if (enums instanceof ArrayList) {
this.enums = enums;
} else {
getEnums().addAll(enums);
}
return this;
}
/**
* 消息包含的Oneof的列表
*
* @return the oneofs
*/
public List<Oneof> getOneofs() {
if (oneofs == null) {
oneofs = new ArrayList<>();
}
return oneofs;
}
/**
* 消息包含的Oneof的列表
*
* @param oneofs the oneofs to set
*/
public ProtoMessage setOneofs(List<Oneof> oneofs) {
if (oneofs instanceof ArrayList) {
this.oneofs = oneofs;
} else {
getOneofs().addAll(oneofs);
}
return this;
}
/**
* 消息的备注信息
*
* @return the comment
*/
public Comment getComment() {
return comment;
}
/**
* 消息的备注信息
*
* @param comment the comment to set
*/
public ProtoMessage setComment(Comment comment) {
this.comment = comment;
return this;
}
}
@@ -0,0 +1,220 @@
/*
* 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.wire;
import com.zfoo.protocol.serializer.protobuf.wire.TagReserved.StartEnd;
import com.zfoo.protocol.serializer.protobuf.wire.parser.ProtoParser;
import java.util.ArrayList;
import java.util.List;
/**
* protocol buffer中预留信息的结构定义
*/
public class Reserved {
/**
* 消息保留Field列表的表达式
*/
private String expression;
/**
* 保留Fields设置的注释信息
*/
private Comment comment;
public Reserved setExpression(String expression) {
this.expression = expression;
return this;
}
public String getExpression() {
return expression;
}
public Reserved setComment(Comment comment) {
this.comment = comment;
return this;
}
public Comment getComment() {
return comment;
}
public static Reserved parseReserved(String expression, List<Comment> comments) throws RuntimeException {
String exp = expression.trim();
if (exp.isEmpty()) {
throw new RuntimeException("reserved expression is empty");
}
Reserved reserved;
if (exp.charAt(0) == '\'' || exp.charAt(0) == '"') {
reserved = new NameReserved().setFieldNames(readFieldNames(exp));
} else {
reserved = readRagReserved(exp);
}
if (!comments.isEmpty()) {
reserved.setComment(comments.get(comments.size() - 1));
comments.clear();
}
reserved.setExpression(exp);
return reserved;
}
public static TagReserved readRagReserved(String expression) throws RuntimeException {
TagReserved reserved = new TagReserved();
String[] stags = expression.split(",");
List<Integer> tags = new ArrayList<>();
List<StartEnd> startEnds = new ArrayList<>();
for (String sTag : stags) {
int index = sTag.indexOf("to");
if (index == -1) {
int tag = ProtoParser.parseInt(sTag.trim());
tags.add(tag);
} else {
int start = ProtoParser.parseInt(sTag.substring(0, index).trim());
int end;
String sEnd = sTag.substring(index + 2).trim();
if (!"max".equalsIgnoreCase(sEnd)) {
end = ProtoParser.parseInt(sTag.substring(index + 2).trim());
} else {
end = WireFormat.MAX_TAG_VALUE;
}
StartEnd se = new StartEnd();
se.setStartTag(start).setEndTag(end);
startEnds.add(se);
}
}
reserved.setTags(tags);
reserved.setStartEnds(startEnds);
return reserved;
}
public static List<String> readFieldNames(String exp) throws RuntimeException {
ParseFieldNameResult res = readFieldName(exp, 0);
char endChar = res.getEndChar();
List<String> names = new ArrayList<>();
while (endChar != ';') {
names.add(res.getName());
if (res.getPos() >= exp.length()) {
return names;
}
res.setPos(res.getPos() + 1);
trim(exp, res);
if (res.getPos() < exp.length()) {
res = readFieldName(exp, res.getPos());
endChar = res.getEndChar();
if (endChar != ',' && endChar != '\'' && endChar != '"') {
break;
}
}
}
throw new RuntimeException("reserved not end \";\"");
}
private static ParseFieldNameResult readFieldName(String exp, int pos)
throws RuntimeException {
ParseFieldNameResult result = new ParseFieldNameResult();
result.setPos(pos);
trim(exp, result);
char startChar = result.getEndChar();
if (startChar != '\'' && startChar != '"') {
throw new RuntimeException("field name not start with ' or \"");
}
StringBuilder sb = new StringBuilder();
int start = result.getPos();
char c;
for (int i = start + 1; i < exp.length(); i++) {
c = exp.charAt(i);
if (c == startChar) {
if (sb.length() > 0) {
result.setPos(i + 1);
trim(exp, result);
result.setName(sb.toString());
return result;
} else {
result.setName(sb.toString());
return result;
}
} else {
sb.append(c);
}
}
return result;
}
private static void trim(String exp, final ParseFieldNameResult result) {
int pos = result.getPos();
char c;
for (int i = pos; i < exp.length(); i++) {
c = exp.charAt(i);
if (c != ' ' && c != '\t') {
result.setPos(i);
result.setEndChar(c);
return;
}
}
}
static class ParseFieldNameResult {
private String name;
private int pos;
private char endChar;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public ParseFieldNameResult setName(String name) {
this.name = name;
return this;
}
/**
* @return the pos
*/
public int getPos() {
return pos;
}
/**
* @param pos the pos to set
*/
public ParseFieldNameResult setPos(int pos) {
this.pos = pos;
return this;
}
/**
* @return the endChar
*/
public char getEndChar() {
return endChar;
}
/**
* @param endChar the endChar to set
*/
public ParseFieldNameResult setEndChar(char endChar) {
this.endChar = endChar;
return this;
}
}
}
@@ -0,0 +1,44 @@
/*
* 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.wire;
/**
* protocol buffer的协议版本
*/
public enum Syntax {
PROTO_2("proto2"),
PROTO_3("proto3");
private final String value;
Syntax(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static Syntax fromValue(String value) {
switch (value) {
case "proto2":
return Syntax.PROTO_2;
case "proto3":
return Syntax.PROTO_3;
default:
throw new IllegalArgumentException("no enum value Syntax " + value);
}
}
}
@@ -0,0 +1,108 @@
/*
* 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.wire;
import java.util.List;
/**
* 按Tag的编号预留的结构定义
*/
public class TagReserved extends Reserved {
/**
* 消息保留的tag列表
*/
private List<Integer> tags;
/**
* 保留tag的开始和结束范围列表
*/
private List<StartEnd> startEnds;
/**
* 消息保留的tag列表
*
* @return the tags
*/
public List<Integer> getTags() {
return tags;
}
/**
* 消息保留的tag列表
*
* @param tags the tags to set
* @return
*/
public TagReserved setTags(List<Integer> tags) {
this.tags = tags;
return this;
}
/**
* 保留tag的开始和结束范围列表
*
* @return the startEnds
*/
public List<StartEnd> getStartEnds() {
return startEnds;
}
/**
* 保留tag的开始和结束范围列表
*
* @param startEnds the startEnds to set
* @return
*/
public TagReserved setStartEnds(List<StartEnd> startEnds) {
this.startEnds = startEnds;
return this;
}
public static class StartEnd {
private int startTag;
private int endTag;
/**
* @return the startTag
*/
public int getStartTag() {
return startTag;
}
/**
* @param startTag the startTag to set
* @return
*/
public StartEnd setStartTag(int startTag) {
this.startTag = startTag;
return this;
}
/**
* @return the endTag
*/
public int getEndTag() {
return endTag;
}
/**
* @param endTag the endTag to set
* @return
*/
public StartEnd setEndTag(int endTag) {
this.endTag = endTag;
return this;
}
}
}
@@ -0,0 +1,153 @@
/*
* 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.wire;
/**
* 协议格式定义相关的常量等
*/
public class WireFormat {
private WireFormat() {
}
/**
* 定长32位占用的字节长度
*/
public static final int FIXED_32_SIZE = 4;
/**
* 定长64位占用的字节长度
*/
public static final int FIXED_64_SIZE = 8;
/**
* 变长整型数据最大査勇的字节数
*/
public static final int MAX_VARINT_SIZE = 5;
/**
* 定长长整型最大占用的字节数
*/
public static final int MAX_VARLONG_SIZE = 10;
/**
* 最小的Tag值
*/
public static final int MIN_TAG_VALUE = 1;
/**
* 最大的Tag值
*/
public static final int MAX_TAG_VALUE = (1 << 29) - 1; // 536,870,911
/**
* 系统预留Tag的开始值
*/
public static final int RESERVED_TAG_VALUE_START = 19000;
/**
* 系统预留Tag的结束值
*/
public static final int RESERVED_TAG_VALUE_END = 19999;
/**
* Tag的数据类型占用的bit个数
*/
static final int TAG_TYPE_BITS = 3;
/**
* Tag类型的掩码
*/
static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1;
/**
* 空的byte数组
*/
static final byte[] EMPTY_BYTES = new byte[0];
/**
* Tag值是否符合规则
*
* @param tag 给定的Tag值
* @return 是否符合规则
*/
public static boolean isValidTag(int tag) {
return (tag >= MIN_TAG_VALUE && tag < RESERVED_TAG_VALUE_START)
|| (tag > RESERVED_TAG_VALUE_END && tag <= MAX_TAG_VALUE);
}
/**
* 从int值盅获取该Tag的数据类型的int值
*
* @param tag 给定的int值
* @return
*/
public static int getTagWireType(final int tag) {
return tag & TAG_TYPE_MASK;
}
/**
* 获取给定的int值中Tag的值
*
* @param tag 解码出的tag的int值
* @return
*/
public static int getTagFieldNumber(final int tag) {
return tag >>> TAG_TYPE_BITS;
}
/**
* 根据属性的Tag值,以及数据类型,获取一个编码后的int值
*
* @param tagNum tag的编号
* @param wireType 数据类型
* @return
*/
public static int makeTag(final int tagNum, final WireType wireType) {
return (tagNum << TAG_TYPE_BITS) | wireType.getValue();
}
/**
* java的数据类型和protocol buffer的数据类型的对应关系以及默认值
*/
public enum JavaType {
INT("int", "Integer", 0),
LONG("long", "Long", 0L),
FLOAT("float", "Float", 0F),
DOUBLE("double", "Double", 0D),
BOOLEAN("boolean", "Boolean", false),
STRING("String", "String", ""),
BYTES("byte[]", "byte[]", EMPTY_BYTES),
ENUM("enum", "enum", null),
MESSAGE("", "", null),
OBJECT("Object", "Object", null),
MAP("Map", "Map", null);
JavaType(final String typeString, final String boxedType, final Object defaultValue) {
this.typeString = typeString;
this.boxedType = boxedType;
this.defaultValue = defaultValue;
}
public String getTypeString() {
return this.typeString;
}
/**
* The default value for fields of this type, if it's a primitive type.
*/
public Object defaultValue() {
return this.defaultValue;
}
public String getBoxedType() {
return this.boxedType;
}
private final Object defaultValue;
private final String typeString;
private final String boxedType;
}
}
@@ -0,0 +1,81 @@
/*
* 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.wire;
/**
* protocol buffer的协议数据类型
*/
public enum WireType {
/**
* 变长整数
*/
VARINT(0),
/**
* 定长64位
*/
FIXED64(1),
/**
* 指定长度的编码类型
*/
LENGTH_DELIMITED(2),
/**
* 组开始标记
*/
START_GROUP(3),
/**
* 组结束标记
*/
END_GROUP(4),
/**
* 定长32位
*/
FIXED32(5),
/**
* OBJECT类型,可以包含任何类型的数据由OBJECT编解码器进行编解码
*/
OBJECT(6);
WireType(int value) {
this.value = value;
}
private final int value;
public int getValue() {
return value;
}
public static WireType fromValue(int value) {
switch (value) {
case 0:
return VARINT;
case 1:
return FIXED64;
case 2:
return LENGTH_DELIMITED;
case 3:
return START_GROUP;
case 4:
return END_GROUP;
case 5:
return FIXED32;
case 6:
return OBJECT;
default:
throw new IllegalArgumentException("no enum value WireType " + value);
}
}
}
@@ -0,0 +1,247 @@
/*
* 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.wire.parser;
import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.serializer.protobuf.wire.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* proto文件的结构定义
*/
public class Proto {
/**
* Proto描述文件的名称
*/
private String name;
/**
* Proto的可选信息
*/
private List<Option> options = new ArrayList<>();
/**
* 文档版本没有指定默认为proto2
*/
private Syntax syntax = Syntax.PROTO_3;
/**
* proto文件的包名
*/
private String protoPackage;
/**
* 定义的消息列表
*/
private List<ProtoMessage> protoMessages = new ArrayList<>();
/**
* 文档导入的文件的路径列表
*/
private List<String> imports = new ArrayList<>();
/**
* 文档的注释
*/
private String comment;
/**
* 消息内置的枚举类型
*/
private List<ProtoEnum> enums = new ArrayList<>();
/**
* 消息内嵌的扩展的消息列表
*/
private List<Extend> protoExtends = new ArrayList<>();
/**
* proto文件的单行注释列表
*/
private List<String> comments = new ArrayList<>();
/**
* 该文件的来源的File对象
*/
private File file;
public Proto setSyntax(Syntax syntax) {
this.syntax = syntax;
return this;
}
public Proto addOption(Option option) {
getOptions().add(option);
return this;
}
public Proto addImport(String value) {
getImports().add(value);
return this;
}
public Proto addMsg(ProtoMessage msg) {
getMessages().add(msg);
return this;
}
public Proto addEnum(ProtoEnum protoEnum) {
getEnums().add(protoEnum);
return this;
}
public Proto addExtend(Extend extend) {
if (extend == null) {
return this;
}
getProtoExtends().add(extend);
return this;
}
/**
* Proto描述文件的名称
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Proto描述文件的名称
*
* @param name the name to set
* @return
*/
public Proto setName(String name) {
this.name = name;
return this;
}
/**
* Proto的可选信息
*
* @return the options
*/
public List<Option> getOptions() {
return options;
}
/**
* 文档版本没有指定默认为proto2
*
* @return the syntax
*/
public Syntax getSyntax() {
return syntax;
}
/**
* proto文件的包名
*
* @return the protoPackage
*/
public String getProtoPackage() {
return protoPackage;
}
/**
* proto文件的包名
*
* @param protoPackage the protoPackage to set
* @return
*/
public Proto setProtoPackage(String protoPackage) {
this.protoPackage = protoPackage;
return this;
}
/**
* 定义的消息列表
*
* @return the messages
*/
public List<ProtoMessage> getMessages() {
return protoMessages;
}
/**
* 文档导入的文件的路径列表
*
* @return the imports
*/
public List<String> getImports() {
return imports;
}
/**
* 文档的注释
*
* @return the comment
*/
public String getComment() {
return comment;
}
/**
* 文档的注释
*
* @param comment the comment to set
* @return
*/
public Proto setComment(String comment) {
this.comment = comment;
return this;
}
/**
* 消息内置的枚举类型
*
* @return the enums
*/
public List<ProtoEnum> getEnums() {
return enums;
}
/**
* 消息内嵌的扩展的消息列表
*
* @return the protoExtends
*/
public List<Extend> getProtoExtends() {
return protoExtends;
}
/**
* proto文件的单行注释列表
*
* @param comments the comments to set
*/
public void addComments(List<String> comments) {
if (CollectionUtils.isEmpty(comments)) {
return;
}
this.comments.addAll(comments);
}
/**
* @return the file
*/
public File getFile() {
return file;
}
/**
* @param file the file to set
*/
public void setFile(File file) {
this.file = file;
}
}
@@ -420,7 +420,7 @@ public abstract class FileUtils {
public static String readFileToString(final File file) {
return StringUtils.joinWith(StringUtils.EMPTY, readFileToStringList(file).toArray());
return StringUtils.joinWith(LS, readFileToStringList(file).toArray());
}
public static List<String> readFileToStringList(final File file) {
@@ -0,0 +1,34 @@
/*
* 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.protobuf;
import com.zfoo.protocol.serializer.protobuf.GeneratePbUtils;
import com.zfoo.protocol.serializer.protobuf.PbBuildOption;
import com.zfoo.protocol.util.ThreadUtils;
import org.junit.Test;
public class AllTypeBuilderTest {
@Test
public void testAllTypeTest() {
var buildOption = new PbBuildOption();
buildOption.setProtoPath("D:\\github\\zfoo\\protocol\\src\\test\\resources\\proto");
// buildOption.setProtoPath("D:\\github\\zoo\\app\\zgame\\zgame-common\\src\\main\\resources\\proto");
buildOption.setOutputPath("D:\\github\\zfoo\\protocol\\src\\test\\tmpsrc/");
GeneratePbUtils.create(buildOption);
ThreadUtils.sleep(1000);
}
}
@@ -0,0 +1,31 @@
syntax = "proto3";
package test.message;
import "one_enum.proto";
import "one_map.proto";
import "one_message.proto";
option java_package = "io.edap.protobuf.test.message.v3";
//option java_outer_classname = "MessageOuter";
message AllType {
bool field1 = 1;
bytes field2 = 2;
double field3 = 3;
Corpus field4 = 4;
fixed32 field5 = 5;
fixed64 field6 = 6;
float field7 = 7;
int32 field8 = 8;
int64 field9 = 9;
map<string, Project> field10 = 10;
Proj field11 = 11;
sfixed32 field12 = 12;
sfixed64 field13 = 13;
sint32 field14 = 14;
sint64 field15 = 15;
string field16 = 16;
uint32 field17 = 17;
uint64 field18 = 18;
}
@@ -0,0 +1,21 @@
syntax = "proto3";
package test.message;
option java_package = "io.edap.protobuf.test.message.v3";
message OneEnum {
Corpus corpus = 1;
}
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
@@ -0,0 +1,16 @@
syntax = "proto3";
package test.message;
option java_package = "io.edap.protobuf.test.message.v3";
message OneMap {
map<string, Project> value = 1;
}
message Project {
int64 id = 1;
string name = 2;
string repoPath = 3;
}
@@ -0,0 +1,18 @@
syntax = "proto3";
package test.message;
option java_package = "io.edap.protobuf.test.message.v3";
message OneMessage {
Proj value = 1;
}
message Proj {
int64 id = 1;
string name = 2;
string repoPath = 3;
//zzzzzzz
repeated int32 a = 4;//rrrrrrrrrrrrrrrrrrrrrrr
}