package io.anemos.metastore.putils; import com.google.common.base.Strings; import com.google.protobuf.ByteString; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.Descriptors; import com.google.protobuf.DynamicMessage; import com.google.protobuf.Message; import com.google.protobuf.UnknownFieldSet; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class ProtoLanguageFileWriter { private Descriptors.FileDescriptor fd; private ProtoDomain domain; private ProtoLanguageFileWriter(Descriptors.FileDescriptor fileDescriptor, ProtoDomain domain) { this.fd = fileDescriptor; this.domain = domain; if (domain == null) { this.domain = new ProtoDomain(); } } private ProtoLanguageFileWriter(Descriptors.FileDescriptor fileDescriptor) { this(fileDescriptor, null); } public static void write( Descriptors.FileDescriptor fd, ProtoDomain PContainer, OutputStream outputStream) { PrintWriter printWriter = new PrintWriter(outputStream); new ProtoLanguageFileWriter(fd, PContainer).write(printWriter); printWriter.flush(); } public static String write(Descriptors.FileDescriptor fd, ProtoDomain PContainer) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); write(fd, PContainer, byteArrayOutputStream); return byteArrayOutputStream.toString(); } public static void write(Descriptors.FileDescriptor fd, OutputStream outputStream) { PrintWriter printWriter = new PrintWriter(outputStream); new ProtoLanguageFileWriter(fd).write(printWriter); printWriter.flush(); } public static String write(Descriptors.FileDescriptor fd) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); write(fd, byteArrayOutputStream); return byteArrayOutputStream.toString(); } private static String unsignedToString(final long value) { if (value >= 0) { return Long.toString(value); } else { // Pull off the most-significant bit so that BigInteger doesn't think // the number is negative, then set it again using setBit(). return BigInteger.valueOf(value & 0x7FFFFFFFFFFFFFFFL).setBit(63).toString(); } } public void write(PrintWriter writer) { new ProtoFilePrintWriter(writer).write(); } private class ProtoFilePrintWriter { private PrintWriter writer; private CommentIndexer commentIndexer; public ProtoFilePrintWriter(PrintWriter writer) { this.writer = writer; this.commentIndexer = new CommentIndexer(); } private void extensions() { Map<String, List<Descriptors.FieldDescriptor>> extensions = new HashMap<>(); for (Descriptors.FieldDescriptor fieldDescriptor : fd.getExtensions()) { String toExtend = fieldDescriptor.getContainingType().getFullName(); if (extensions.containsKey(toExtend)) { extensions.get(toExtend).add(fieldDescriptor); } else { ArrayList<Descriptors.FieldDescriptor> fdList = new ArrayList<>(); fdList.add(fieldDescriptor); extensions.put(toExtend, fdList); } } for (String toExtend : extensions.keySet()) { writer.println(); writer.println("extend " + toExtend + " {"); for (Descriptors.FieldDescriptor fieldDescriptor : extensions.get(toExtend)) { writeField(fieldDescriptor, new PathLocation(), 1); } writer.println("}\n"); } } private void indent(int indent) { for (int i = 0; i < indent; i++) { writer.print("\t"); } } private boolean isMap(Descriptors.FieldDescriptor field) { if (field.getType() != Descriptors.FieldDescriptor.Type.MESSAGE) { return false; } return field.getMessageType().getOptions().getMapEntry(); } private void writeFieldType(Descriptors.FieldDescriptor field) { if (field.isRepeated() && !isMap(field)) { writer.print("repeated "); } switch (field.getType()) { case UINT64: writer.print("uint64"); break; case INT32: writer.print("int32"); break; case INT64: writer.print("int64"); break; case FIXED64: writer.print("fixed64"); break; case FIXED32: writer.print("fixed32"); break; case BOOL: writer.print("bool"); break; case STRING: writer.print("string"); break; case GROUP: // TODO figure out if we need to support this (proto2) writer.print("GROUP"); break; case MESSAGE: { Descriptors.Descriptor messageType = field.getMessageType(); if (messageType.getFile() == fd) { if (isMap(field)) { writer.print("map<"); writeFieldType(messageType.findFieldByNumber(1)); writer.print(", "); writeFieldType(messageType.findFieldByNumber(2)); writer.print(">"); } else { writer.print(messageType.getName()); } } else { writer.print(messageType.getFullName()); } break; } case BYTES: writer.print("bytes"); break; case UINT32: writer.print("uint32"); break; case ENUM: { Descriptors.EnumDescriptor enumType = field.getEnumType(); if (enumType.getFile() == fd) { writer.print(enumType.getName()); } else { writer.print(enumType.getFullName()); } break; } case SFIXED32: writer.print("sfixed32"); break; case SFIXED64: writer.print("sfixed64"); break; case SINT32: writer.print("sint32"); break; case SINT64: writer.print("sint64"); break; case DOUBLE: writer.print("double"); break; case FLOAT: writer.print("float"); break; } } private void writeField(Descriptors.FieldDescriptor field, PathLocation parent, int indent) { PathLocation location = parent.addField(field); writeLeadingComment(commentIndexer.getLocation(location), indent); indent(indent); writeFieldType(field); writer.print(" "); writer.print(field.getName()); writer.print(" = "); writer.print(field.getNumber()); writeOptionsForList(field.getOptions(), indent, "Field"); writer.print(";"); writeTrailingComment(commentIndexer.getLocation(location), indent); } private void writeOptionsForList( com.google.protobuf.GeneratedMessageV3.ExtendableMessage options, int indent, String optionType) { Map<Integer, Descriptors.FieldDescriptor> unknownMap; switch (optionType) { case "Field": unknownMap = domain.getOptions().getFieldOptionMap(); break; case "EnumValue": unknownMap = domain.getOptions().getEnumValueOptionMap(); break; default: throw new RuntimeException("Exception"); } Map<Descriptors.FieldDescriptor, Object> resolved = new LinkedHashMap<>(); resolved.putAll(options.getAllFields()); resolved.putAll(convertUnknownFieldValue(options.getUnknownFields(), unknownMap)); if (resolved.size() > 0) { writer.print(" [\n"); Iterator<Map.Entry<Descriptors.FieldDescriptor, Object>> iterator = resolved.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Descriptors.FieldDescriptor, Object> fieldOption = iterator.next(); Descriptors.FieldDescriptor fieldDescriptor = fieldOption.getKey(); Object value = fieldOption.getValue(); if (fieldDescriptor.isRepeated()) { List values = (List) value; for (int i = 0; i < values.size(); i++) { Object o = values.get(i); indent(indent + 1); writeOptionForList(fieldDescriptor, o, indent, optionType); if (i < values.size() - 1) { writer.print(","); } writer.println(); } } else { indent(indent + 1); writeOptionForList(fieldDescriptor, value, indent, optionType); if (iterator.hasNext()) { writer.print(","); } writer.println(); } } indent(indent); writer.print("]"); } } private String value(Descriptors.FieldDescriptor fd, Object value) { StringBuilder stringBuilder = new StringBuilder(); if (fd.getType() == Descriptors.FieldDescriptor.Type.STRING) { stringBuilder.append("\""); stringBuilder.append(simpleEscape(value)); stringBuilder.append("\""); } else { stringBuilder.append(value); } return stringBuilder.toString(); } private String simpleEscape(Object value) { if (value == null) { return null; } StringBuilder escapedValue = new StringBuilder(); for (char c : ((String) value).toCharArray()) { switch (c) { case '\t': escapedValue.append("\\t"); break; case '\n': escapedValue.append("\\n"); break; case '\\': escapedValue.append("\\\\"); break; case '"': escapedValue.append("\\\""); break; default: escapedValue.append(c); } } return escapedValue.toString(); } private void writeValue(Descriptors.FieldDescriptor fd, Object value) { if (fd.isRepeated() && value instanceof List) { List values = (List) value; List<String> stringList = new ArrayList<>(); writer.print('['); values.forEach(v -> stringList.add(value(fd, v))); writer.print(String.join(",", stringList)); writer.print(']'); } else { writer.print(value(fd, value)); } } private List<String> lines(String block) { List<String> lines = new ArrayList<>(); for (String s : block.split("\n")) { lines.add(s); } return lines; } private void writeComment(String block, int indent) { lines(block) .forEach( line -> { indent(indent); writer.print("//"); writer.println(line); }); } private void writeLeadingComment( DescriptorProtos.SourceCodeInfo.Location location, int indent) { if (location != null) { location .getLeadingDetachedCommentsList() .forEach( block -> { writeComment(block, indent); writer.println(); }); writeComment(location.getLeadingComments(), indent); } } private void writeTrailingComment( DescriptorProtos.SourceCodeInfo.Location location, int indent) { if (location != null && location.hasTrailingComments()) { writer.println(); writeComment(location.getTrailingComments(), indent); } else { writer.println(); } } private void writeMessageValue(Message v, int indent) { writer.println("{"); v.getAllFields() .forEach( (fieldDescriptor, value) -> { if (value instanceof List) { if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) { // Map handling for (Object message : ((List) value)) { indent(indent + 1); writer.print(fieldDescriptor.getName()); writer.print(": "); writeMessageValue((Message) message, indent + 1); writer.println(); } } else { indent(indent + 1); writer.print(fieldDescriptor.getName()); writer.print(": "); writeValue(fieldDescriptor, value); writer.println(); } } else { indent(indent + 1); writer.print(fieldDescriptor.getName()); writer.print(": "); if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) { writeMessageValue((Message) value, indent + 1); } else { writeValue(fieldDescriptor, value); } writer.println(); } }); indent(indent); writer.print("}"); } private void write() { PathLocation location = new PathLocation(); writeLeadingComment(commentIndexer.getSyntaxLocation(), 0); switch (fd.getSyntax()) { case PROTO2: writer.print("syntax = \"proto2\";"); break; case PROTO3: writer.print("syntax = \"proto3\";"); break; default: break; } writeTrailingComment(commentIndexer.getSyntaxLocation(), 0); writer.println(); if (!fd.getPackage().isEmpty()) { writeLeadingComment(commentIndexer.getPackageLocation(), 0); writer.print("package "); writer.print(fd.getPackage()); writer.print(";"); writeTrailingComment(commentIndexer.getPackageLocation(), 0); writer.println(); } List<Descriptors.FileDescriptor> dependencies = fd.getDependencies(); if (dependencies.size() > 0) { int index = 0; for (Descriptors.FileDescriptor dependency : dependencies) { writeLeadingComment(commentIndexer.importLocations.get(index++), 0); writer.print("import \""); writer.print(dependency.getName()); writer.print("\";"); writeTrailingComment(commentIndexer.importLocations.get(index++), 0); } writer.println(); } writeOptionsForBlock(fd.getOptions(), 0, "File"); extensions(); for (Descriptors.EnumDescriptor enumDescriptor : fd.getEnumTypes()) { writer.println(); writeEnumDescriptor(enumDescriptor, location, 0); } for (Descriptors.ServiceDescriptor serviceDescriptor : fd.getServices()) { writer.println(); writeServiceDescriptor(serviceDescriptor, location); } for (Descriptors.Descriptor messageType : fd.getMessageTypes()) { writer.println(); writeMessageDescriptor(messageType, location, 0); } } private void writeServiceDescriptor( Descriptors.ServiceDescriptor serviceDescriptor, PathLocation parent) { PathLocation location = parent.addService(serviceDescriptor); writeLeadingComment(commentIndexer.getLocation(location), 0); writer.print("service "); writer.print(serviceDescriptor.getName()); writer.println(" {"); writeOptionsForBlock(serviceDescriptor.getOptions(), 1, "Service"); for (Descriptors.MethodDescriptor method : serviceDescriptor.getMethods()) { PathLocation methodLocation = location.addMethod(method); writeLeadingComment(commentIndexer.getLocation(methodLocation), 1); indent(1); writer.print("rpc "); writer.print(method.getName()); writer.print("("); if (method.isClientStreaming()) { writer.print("stream "); } writer.print(method.getInputType().getFullName()); writer.print(") returns ("); if (method.isServerStreaming()) { writer.print("stream "); } writer.print(method.getOutputType().getFullName()); DescriptorProtos.MethodOptions options = method.getOptions(); if (options.getAllFields().size() == 0 && options.getUnknownFields().asMap().size() == 0) { writer.print(") {}"); } else { writer.println(") {"); writeOptionsForBlock(options, 2, "Method"); indent(1); writer.print("}"); } writeTrailingComment(commentIndexer.getLocation(methodLocation), 1); } writer.print("}"); writeTrailingComment(commentIndexer.getLocation(location), 0); } private void writeEnumDescriptor( Descriptors.EnumDescriptor enumType, PathLocation parent, int indent) { PathLocation location = parent.addEnum(enumType); writeLeadingComment(commentIndexer.getLocation(location), indent); indent(indent); writer.print("enum "); writer.print(enumType.getName()); writer.println(" {"); writeOptionsForBlock(enumType.getOptions(), indent + 1, "Enum"); for (Descriptors.EnumValueDescriptor value : enumType.getValues()) { indent(indent + 1); writer.print(value.getName()); writer.print(" = "); writer.print(value.getNumber()); writeOptionsForList(value.getOptions(), indent + 1, "EnumValue"); writer.println(";"); } indent(indent); writer.print("}"); writeTrailingComment(commentIndexer.getLocation(location), indent); } private void writeMessageDescriptor( Descriptors.Descriptor messageType, PathLocation parent, int indent) { PathLocation location = parent.addMessage(messageType); writeLeadingComment(commentIndexer.getLocation(location), indent); indent(indent); writer.print("message "); writer.print(messageType.getName()); writer.println(" {"); writeOptionsForBlock(messageType.getOptions(), indent + 1, "Message"); for (Descriptors.Descriptor nestedType : messageType.getNestedTypes()) { if (!nestedType.getOptions().getMapEntry()) { writeMessageDescriptor(nestedType, location, indent + 1); writer.println(); } } for (Descriptors.EnumDescriptor enumType : messageType.getEnumTypes()) { writeEnumDescriptor(enumType, location, indent + 1); writer.println(); } Map<Integer, Descriptors.OneofDescriptor> oneofmap = new HashMap<>(); for (Descriptors.OneofDescriptor oneof : messageType.getOneofs()) { oneofmap.put(oneof.getField(0).getIndex(), oneof); } List<Descriptors.FieldDescriptor> fields = messageType.getFields(); for (int ix = 0; ix < fields.size(); ) { Descriptors.FieldDescriptor field = fields.get(ix); Descriptors.OneofDescriptor oneof = oneofmap.get(ix); if (oneof != null) { indent(indent + 1); writer.print("oneof "); writer.print(oneof.getName()); writer.println(" {"); for (Descriptors.FieldDescriptor oneofField : oneof.getFields()) { writeField(oneofField, location, indent + 2); ix++; } indent(indent + 1); writer.println("}"); } else { writeField(field, location, indent + 1); ix++; } } indent(indent); writer.print("}"); writeTrailingComment(commentIndexer.getLocation(location), indent); } private void writeOptionForBlock( Descriptors.FieldDescriptor fieldDescriptor, Object value, int indent, String optionType) { indent(indent); writer.print("option "); if (fieldDescriptor.getFullName().startsWith("google.protobuf." + optionType + "Options")) { writer.print(fieldDescriptor.getName()); } else { writer.print("("); writer.print(fieldDescriptor.getFullName()); writer.print(")"); } writer.print(" = "); if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) { writeMessageValue((Message) value, indent); } else { writeValue(fieldDescriptor, value); } writer.println(";"); } private void writeOptionForList( Descriptors.FieldDescriptor fieldDescriptor, Object value, int indent, String optionType) { if (fieldDescriptor.getFullName().startsWith("google.protobuf." + optionType + "Options")) { writer.print(fieldDescriptor.getName()); } else { writer.print("("); writer.print(fieldDescriptor.getFullName()); writer.print(")"); } writer.print(" = "); if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) { writeMessageValue((Message) value, indent + 1); } else { writeValue(fieldDescriptor, value); } } private void writeOptionsForBlock( com.google.protobuf.GeneratedMessageV3.ExtendableMessage options, int indent, String optionType) { Map<Integer, Descriptors.FieldDescriptor> unknownMap; switch (optionType) { case "File": unknownMap = domain.getOptions().getFileOptionMap(); break; case "Message": unknownMap = domain.getOptions().getMessageOptionMap(); break; case "Enum": unknownMap = domain.getOptions().getEnumOptionMap(); break; case "Service": unknownMap = domain.getOptions().getServiceOptionMap(); break; case "Method": unknownMap = domain.getOptions().getMethodOptionMap(); break; default: throw new RuntimeException("Exception"); } Map<Descriptors.FieldDescriptor, Object> resolved = new LinkedHashMap<>(); resolved.putAll(options.getAllFields()); resolved.putAll(convertUnknownFieldValue(options.getUnknownFields(), unknownMap)); resolved.forEach( (fieldDescriptor, value) -> { if (fieldDescriptor.isRepeated()) { List values = (List) value; values.forEach(v -> writeOptionForBlock(fieldDescriptor, v, indent, optionType)); } else { writeOptionForBlock(fieldDescriptor, value, indent, optionType); } }); writer.println(); } private Object convertFieldValue(Descriptors.FieldDescriptor fieldDescriptor, Object value) { switch (fieldDescriptor.getType()) { case MESSAGE: try { DynamicMessage dynamicMessage = DynamicMessage.parseFrom(fieldDescriptor.getMessageType(), (ByteString) value); return dynamicMessage; } catch (IOException e) { throw new RuntimeException(e); } case BOOL: return value.equals(1L); case ENUM: case STRING: ByteString byteString = (ByteString) value; return byteString.toStringUtf8(); case INT32: case INT64: return unsignedToString((Long) value); case DOUBLE: return Double.longBitsToDouble((Long) value); case FLOAT: return Float.intBitsToFloat((Integer) value); } throw new RuntimeException( "conversion of unknownfield for type " + fieldDescriptor.getType().toString() + " not implemented"); } private Map<Descriptors.FieldDescriptor, Object> convertUnknownFieldValue( UnknownFieldSet unknownFieldSet, Map<Integer, Descriptors.FieldDescriptor> optionsMap) { Map<Descriptors.FieldDescriptor, Object> unknownFieldValues = new LinkedHashMap<>(); unknownFieldSet .asMap() .forEach( (number, field) -> { Descriptors.FieldDescriptor fieldDescriptor = optionsMap.get(number); if (fieldDescriptor.isRepeated()) { unknownFieldValues.put( fieldDescriptor, convertUnknownFieldList(fieldDescriptor, field)); } else { unknownFieldValues.put( fieldDescriptor, convertUnknownFieldValue(fieldDescriptor, field)); } }); return unknownFieldValues; } /** * https://developers.google.com/protocol-buffers/docs/encoding#structure * * @param fieldDescriptor * @param field * @return */ private Object convertUnknownFieldList( Descriptors.FieldDescriptor fieldDescriptor, UnknownFieldSet.Field field) { List list = new ArrayList(); if (field.getLengthDelimitedList().size() > 0) { field .getLengthDelimitedList() .forEach(value -> list.add(convertFieldValue(fieldDescriptor, value))); } if (field.getFixed32List().size() > 0) { field .getFixed32List() .forEach(value -> list.add(convertFieldValue(fieldDescriptor, value))); } if (field.getFixed64List().size() > 0) { field .getFixed64List() .forEach(value -> list.add(convertFieldValue(fieldDescriptor, value))); } if (field.getVarintList().size() > 0) { field.getVarintList().forEach(value -> list.add(convertFieldValue(fieldDescriptor, value))); } if (field.getGroupList().size() > 0) { throw new RuntimeException("Groups are not implemented"); } return list; } private Object convertUnknownFieldValue( Descriptors.FieldDescriptor fieldDescriptor, UnknownFieldSet.Field field) { if (field.getLengthDelimitedList().size() > 0) { if (field.getLengthDelimitedList().size() > 1) { throw new RuntimeException( "Single value should not contrain more then 1 value in the unknown field"); } return convertFieldValue(fieldDescriptor, field.getLengthDelimitedList().get(0)); } if (field.getFixed32List().size() > 0) { if (field.getFixed32List().size() > 1) { throw new RuntimeException( "Single value should not contrain more then 1 value in the unknown field"); } return convertFieldValue(fieldDescriptor, field.getFixed32List().get(0)); } if (field.getFixed64List().size() > 0) { if (field.getFixed64List().size() > 1) { throw new RuntimeException( "Single value should not contrain more then 1 value in the unknown field"); } return convertFieldValue(fieldDescriptor, field.getFixed64List().get(0)); } if (field.getVarintList().size() > 0) { if (field.getVarintList().size() > 1) { throw new RuntimeException( "Single value should not contrain more then 1 value in the unknown field"); } return convertFieldValue(fieldDescriptor, field.getVarintList().get(0)); } if (field.getGroupList().size() > 0) { throw new RuntimeException("Groups are not implemented"); } return null; } } private class PathLocation { private String path; public String toString() { return path; } private PathLocation() { this.path = "]"; } private PathLocation(String path) { this.path = path; } PathLocation add(char t, int index) { return new PathLocation(path + "->" + t + "[" + index + "]"); } PathLocation addMessage(Descriptors.Descriptor type) { return add('M', type.getIndex()); } PathLocation addField(Descriptors.FieldDescriptor type) { return add('f', type.getIndex()); } PathLocation addEnum(Descriptors.EnumDescriptor type) { return add('E', type.getIndex()); } PathLocation addEnunValue(Descriptors.EnumValueDescriptor type) { return add('e', type.getIndex()); } public PathLocation addService(Descriptors.ServiceDescriptor type) { return add('S', type.getIndex()); } public PathLocation addMethod(Descriptors.MethodDescriptor type) { return add('m', type.getIndex()); } } private class CommentIndexer { private DescriptorProtos.SourceCodeInfo.Location syntaxLocation; private DescriptorProtos.SourceCodeInfo.Location packageLocation; private Map<String, DescriptorProtos.SourceCodeInfo.Location> locations; private Map<Integer, DescriptorProtos.SourceCodeInfo.Location> importLocations; private boolean isBlank(String string) { return Strings.nullToEmpty(string).trim().isEmpty(); } private CommentIndexer() { locations = new HashMap<>(); importLocations = new HashMap<>(); fd.toProto() .getSourceCodeInfo() .getLocationList() .forEach( location -> { boolean onlyWhitespace = isBlank(location.getLeadingComments()) && isBlank(location.getTrailingComments()) && location.getLeadingDetachedCommentsCount() == 0; if (!onlyWhitespace) { System.out.println("====="); List<Integer> pathList = location.getPathList(); List<Integer> rest = new ArrayList(); if (location.getPathCount() > 2) { rest = pathList.subList(2, pathList.size()); } String p; switch (location.getPath(0)) { case 2: packageLocation = location; break; case 3: importLocations.put(location.getPath(1), location); break; case 4: p = pathForMessage(new PathLocation().add('M', location.getPath(1)), rest) .toString(); System.out.println(p); locations.put(p, location); break; case 5: p = pathForEnum(new PathLocation().add('E', location.getPath(1)), rest) .toString(); System.out.println(p); locations.put(p, location); break; case 6: p = pathForService(new PathLocation().add('S', location.getPath(1)), rest) .toString(); System.out.println(p); locations.put(p, location); break; case 12: syntaxLocation = location; break; default: System.out.println(location); System.err.println("Unknown path type " + location.getPath(0)); throw new RuntimeException("Unknown path type " + location.getPath(0)); } } }); } private PathLocation pathForMessage(PathLocation root, List<Integer> pathList) { if (pathList.size() == 0) { return root; } List<Integer> rest = new ArrayList(); if (pathList.size() > 2) { rest = pathList.subList(2, pathList.size()); } PathLocation pathLocation = null; switch (pathList.get(0)) { case 2: pathLocation = pathForMessage(root.add('f', pathList.get(1)), rest); break; case 3: pathLocation = pathForMessage(root.add('M', pathList.get(1)), rest); break; default: throw new RuntimeException("Unknown path type " + pathList.get(0)); } return pathLocation; } private PathLocation pathForEnum(PathLocation root, List<Integer> pathList) { if (pathList.size() == 0) { return root; } List<Integer> rest = new ArrayList(); if (pathList.size() > 2) { rest = pathList.subList(2, pathList.size()); } PathLocation pathLocation = null; switch (pathList.get(0)) { case 2: pathLocation = pathForMessage(root.add('v', pathList.get(1)), rest); break; case 3: pathLocation = pathForMessage(root.add('O', pathList.get(1)), rest); break; default: throw new RuntimeException("Unknown path type " + pathList.get(0)); } return pathLocation; } private PathLocation pathForService(PathLocation root, List<Integer> pathList) { if (pathList.size() == 0) { return root; } List<Integer> rest = new ArrayList(); if (pathList.size() > 2) { rest = pathList.subList(2, pathList.size()); } PathLocation pathLocation = null; switch (pathList.get(0)) { case 2: pathLocation = pathForMessage(root.add('m', pathList.get(1)), rest); break; // case 3: // pathLocation = pathForMessage(root.add('e', pathList.get(1)),rest); // break; default: throw new RuntimeException("Unknown path type " + pathList.get(0)); } return pathLocation; } public DescriptorProtos.SourceCodeInfo.Location getPackageLocation() { return packageLocation; } public DescriptorProtos.SourceCodeInfo.Location getSyntaxLocation() { return syntaxLocation; } public DescriptorProtos.SourceCodeInfo.Location getLocation(PathLocation location) { return locations.get(location.toString()); } } }