previous thread: http://groups.google.com/group/protobuf/browse_thread/thread/4bf8bca8c88e82ba/dbaa2803984f3934?lnk=gst&q=debugString()#dbaa2803984f3934
On May 18, 6:05 pm, Ben Wright <compuware...@gmail.com> wrote: > There has been some back and forth previously about java language > support for generating a proto file from a Descriptor. > > The suggested implementation was to either wrap or port the > DebugString capability from decriptor.cc > > I recently ported the DebugString capability to java - it's a bit > rough as it's a by-hand port from the c code, but I also fixed some > bugs in the way the file was generated. For instance, extended types > were not output with correct parenthesis around them and options that > were messages did not have appropriate {} around them - these could > easily be due to differences in the C and Java code though. I'd be > glad to elaborate further for anyone interested, but for now I'm > simply providing the ported code as static functions in a java file. > Notably, if integrated with the java library these functions should > probably not be in a static file, they should be included with their > appropriate descriptor types. > > If it is decided that this functionality should be part of the Java > library, I would be willing to do the leg work and get these functions > in better shape and update the appropriate files. I can pass them > along in an issue report for a committer to check in. > > Notably, if you're looking for someone to work on the Java library / > outstanding issues at all, I'm looking for something to do in my > mythical free time. I could help with c too, but I'm much stronger in > Java. > > PS: This is all based on release 2.4.1 > > ======================= DebugString.java =================== > > import java.io.IOException; > import java.util.ArrayList; > import java.util.HashSet; > import java.util.List; > import java.util.Set; > import java.util.Stack; > > import com.google.protobuf.Descriptors.Descriptor; > import com.google.protobuf.Descriptors.EnumDescriptor; > import com.google.protobuf.Descriptors.EnumValueDescriptor; > import com.google.protobuf.Descriptors.FieldDescriptor; > import com.google.protobuf.Descriptors.FileDescriptor; > import com.google.protobuf.Descriptors.MethodDescriptor; > import com.google.protobuf.Descriptors.ServiceDescriptor; > import com.google.protobuf.Message; > import com.google.protobuf.TextFormat; > > /** > * This class provides .proto file generation from Java Proto > Descriptors. > * <br>Derived from descriptor.cc in Protobuf 2.4.1 > * <br> > * <br><b>Original Copyright Notice from descriptor.cc:</b> > * <br> > * <br> Protocol Buffers - Google's data interchange format > * <br> Copyright 2008 Google Inc. All rights reserved. > * <br>http://code.google.com/p/protobuf/ > * <br> > * <br> Redistribution and use in source and binary forms, with or > without > * <br> modification, are permitted provided that the following > conditions are > * <br> met: > * <br> > * <br> * Redistributions of source code must retain the above > copyright > * <br> notice, this list of conditions and the following disclaimer. > * <br> * Redistributions in binary form must reproduce the above > * <br> copyright notice, this list of conditions and the following > disclaimer > * <br> in the documentation and/or other materials provided with the > * <br> distribution. > * <br> * Neither the name of Google Inc. nor the names of its > * <br> contributors may be used to endorse or promote products > derived from > * <br> this software without specific prior written permission. > * <br> > * <br> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND > CONTRIBUTORS > * <br> "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT > NOT > * <br> LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND > FITNESS FOR > * <br> A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE > COPYRIGHT > * <br> OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, > INCIDENTAL, > * <br> SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT > NOT > * <br> LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS > OF USE, > * <br> DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND > ON ANY > * <br> THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR > TORT > * <br> (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF > THE USE > * <br> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH > DAMAGE. > * <br> > * <br> Author: ken...@google.com (Kenton Varda) > * <br> Based on original Protocol Buffers design by > * <br> Sanjay Ghemawat, Jeff Dean, and others. > * > * @author Benjamin Wright (compuware...@gmail.com) > */ > public class DebugString { > > private static void SubstituteAndAppend(StringBuilder contents, > String replace, Object... objects ) { > for(int i = 0; i < objects.length; i++) { > final String old = "$"+i; > replace = replace.replace(old, > String.valueOf(objects[i])); > } > contents.append(replace); > } > > private static String PrintFieldValueToString(Message message, > FieldDescriptor fieldDescriptor, int i) { > return valueAsString(fieldDescriptor, > message.getField(fieldDescriptor)); > } > > private static String DefaultValueAsString(FieldDescriptor > fieldDescriptor, boolean b) { > return valueAsString(fieldDescriptor, > fieldDescriptor.getDefaultValue()); > } > > private static String valueAsString(FieldDescriptor fieldDescriptor, > Object value) { > StringBuilder v = new StringBuilder(); > try { > TextFormat.printFieldValue(fieldDescriptor, value, v); > } catch (IOException e) { > } > return v.toString();//.replace("\n", "").replace("\\", > "\\\\"); > } > > private static String prefix(int d, char str) { > StringBuilder sb = new StringBuilder(); > > for(int i = 0; i < d; i++) > sb.append(str); > > return sb.toString(); > } > > private static String join(Stack<String> all_options, String string) > { > boolean init = true; > StringBuilder sb = new StringBuilder(); > for(String option : all_options) { > if(init) { > init = false; > } else { > sb.append(string); > } > sb.append(option); > } > return sb.toString(); > } > > // DebugString methods > =============================================== > > // Used by each of the option formatters. > static boolean RetrieveOptions(final Message options, Stack<String> > option_entries) { > option_entries.clear(); > List<FieldDescriptor> fields = new > ArrayList<FieldDescriptor>(options.getAllFields().keySet()); > for (int i = 0; i < fields.size(); i++) { > > int count = 1; > boolean repeated = false; > if (fields.get(i).isRepeated()) { > count = options.getRepeatedFieldCount(fields.get(i)); > repeated = true; > } > for (int j = 0; j < count; j++) { > String fieldval = > PrintFieldValueToString(options, fields.get(i), > repeated ? count : -1); > > // .replace("google.protobuf.FileOptions.", "") > > final boolean msg = (fields.get(i).getJavaType() == > FieldDescriptor.JavaType.MESSAGE); > > final String fullName = fields.get(i).getFullName(); > option_entries.push( > (fields.get(i).isExtension() ? "(" : "") + > (fullName.startsWith("google.protobuf.") ? > fullName.substring(fullName.lastIndexOf('.')+1): fullName) + > (fields.get(i).isExtension() ? ")" : "") + > " = " + > (msg?"{\n":"") + > " "+ fieldval + > (msg?"}":"") ); > } > } > return !option_entries.isEmpty(); > } > > // Formats options that all appear together in brackets. Does not > include > // brackets. > static boolean FormatBracketedOptions(final Message options, > StringBuilder output) { > Stack<String> all_options = new Stack<String>(); > if (RetrieveOptions(options, all_options)) { > output.append(join(all_options, ",\n")); > } > return !all_options.isEmpty(); > } > > // Formats options one per line > static boolean FormatLineOptions(int depth, final Message options, > StringBuilder output) { > String prefix = prefix(depth * 2, ' '); > Stack<String> all_options = new Stack<String>(); > if (RetrieveOptions(options, all_options)) { > for (int i = 0; i < all_options.size(); i++) { > SubstituteAndAppend(output, "$0option $1;\n", > prefix, all_options.get(i)); > } > } > return !all_options.isEmpty(); > } > > // } // anonymous namespace > > /** > * Top level return function > */ > static public StringBuilder FileDescriptor_DebugString(FileDescriptor > fd) { > return FileDescriptor_DebugString(fd, false); > } > /** > * Top level return function > */ > static public StringBuilder FileDescriptor_DebugString(FileDescriptor > fd, boolean includeProtocolSyntaxVersion) { > StringBuilder contents = new StringBuilder(); > > if(includeProtocolSyntaxVersion) { > contents.append("syntax = \"proto2\";\n\n"); > } > > if (!fd.getPackage().isEmpty()) { > SubstituteAndAppend(contents, "package $0;\n\n", > fd.getPackage()); > } > > for (FileDescriptor dependency : fd.getDependencies()) { > SubstituteAndAppend(contents, "import \"$0\";\n", > dependency.getName()); > } > > if(!fd.getDependencies().isEmpty()){ > contents.append("\n"); > } > > if (FormatLineOptions(0, fd.getOptions(), contents)) { > contents.append("\n"); // add some space if we had options > } > > for (EnumDescriptor enum_type : fd.getEnumTypes()) { > EnumDescriptor_DebugString(enum_type, 0, contents); > contents.append("\n"); > } > > // Find all the 'group' type extensions; we will not output their > nested > // definitions (those will be done with their group field > descriptor). > Set<Descriptor> groups = new HashSet<Descriptor>(); > for (FieldDescriptor extension : fd.getExtensions()) { > if (extension.getType() == FieldDescriptor.Type.GROUP) { > groups.add(extension.getMessageType()); > } > } > > for (Descriptor message_type : fd.getMessageTypes()) { > if (!groups.contains(message_type)) { > SubstituteAndAppend(contents, "message $0", > message_type.getName()); > Descriptor_DebugString(message_type, 0, contents); > contents.append("\n"); > } > } > > for (ServiceDescriptor service : fd.getServices()) { > ServiceDescriptor_DebugString(service, contents); > contents.append("\n"); > } > > Descriptor containing_type = null; > for (FieldDescriptor extension : fd.getExtensions()) { > if (extension.getContainingType() != containing_type) { > if (containing_type != null) contents.append("}\n\n"); > containing_type = extension.getContainingType(); > SubstituteAndAppend(contents, "extend $0 {\n", > containing_type.getFullName()); > } > FieldDescriptor_DebugString(extension, 1, contents); > } > if (fd.getExtensions().size() > 0) contents.append("}\n\n"); > > return contents; > } > > static public StringBuilder Descriptor_DebugString(Descriptor d) { > StringBuilder contents = new StringBuilder(); > SubstituteAndAppend(contents, "message $0", d.getName()); > Descriptor_DebugString(d, 0, contents); > return contents; > } > > static void Descriptor_DebugString(Descriptor d, int depth, > StringBuilder contents) { > String prefix = prefix(depth * 2, ' '); > ++depth; > contents.append(" {\n"); > > FormatLineOptions(depth, d.getOptions(), contents); > > // Find all the 'group' types for fields and extensions; we will > not output > // their nested definitions (those will be done with their group > field > // descriptor). > Set<Descriptor> groups = new HashSet<Descriptor>(); > for (int i = 0; i < d.getFields().size(); i++) { > if (d.getFields().get(i).getType() == FieldDescriptor.Type.GROUP) > { > groups.add(d.getFields().get(i).getMessageType()); > } > } > for (int i = 0; i < d.getExtensions().size(); i++) { > if (d.getExtensions().get(i).getType() == > FieldDescriptor.Type.GROUP) { > groups.add(d.getExtensions().get(i).getMessageType()); > } > } > > for (int i = 0; i < d.getNestedTypes().size(); i++) { > if (!groups.contains(d.getNestedTypes().get(i))) { > SubstituteAndAppend(contents, "$0 message $1", > prefix, > d.getNestedTypes().get(i).getName()); > Descriptor_DebugString(d.getNestedTypes().get(i), depth, > contents); > } > } > for (int i = 0; i < d.getEnumTypes().size(); i++) { > EnumDescriptor_DebugString(d.getEnumTypes().get(i), depth, > contents); > } > for (int i = 0; i < d.getFields().size(); i++) { > FieldDescriptor_DebugString(d.getFields().get(i), depth, > contents); > } > > final int max = 536870911; > > for (int i = 0; i < d.toProto().getExtensionRangeCount(); i++) { > final int end = d.toProto().getExtensionRange(i).getEnd() - > 1; > SubstituteAndAppend(contents, "$0 extensions $1 to $2;\n", > prefix, > > d.toProto().getExtensionRange(i).getStart(), > (end == max ? "max" : end) > ); > } > > // Group extensions by what they extend, so they can be printed out > together. > Descriptor containing_type = null; > for (int i = 0; i < d.getExtensions().size(); i++) { > if (d.getExtensions().get(i).getContainingType() != > containing_type) { > if (i > 0) SubstituteAndAppend(contents, "$0 }\n", prefix); > containing_type = d.getExtensions().get(i).getContainingType(); > SubstituteAndAppend(contents, "$0 extend .$1 {\n", > prefix, > containing_type.getFullName()); > } > FieldDescriptor_DebugString(d.getExtensions().get(i), depth + 1, > contents); > } > if (d.getExtensions().size() > 0) > SubstituteAndAppend(contents, "$0 }\n", prefix); > > SubstituteAndAppend(contents, "$0}\n", prefix); > } > > static public StringBuilder > FieldDescriptor_DebugString(FieldDescriptor d) { > StringBuilder contents = new StringBuilder(); > int depth = 0; > if (d.isExtension()) { > SubstituteAndAppend(contents, "extend .$0 {\n", > > d.getContainingType().getFullName()); > depth = 1; > } > FieldDescriptor_DebugString(d, depth, contents); > if (d.isExtension()) { > contents.append("}\n"); > } > return contents; > } > > static void FieldDescriptor_DebugString(FieldDescriptor d, int depth, > StringBuilder contents) { > final String pckg = d.getFile().getPackage(); > String prefix = prefix(depth * 2, ' '); > String field_type; > switch (d.getType()) { > case MESSAGE: > field_type = > d.getMessageType().getFile().getPackage().equals(pckg) ? > d.getMessageType().getName() : d.getMessageType().getFullName(); > break; > case ENUM: > field_type = > d.getEnumType().getFile().getPackage().equals(pckg) ? > d.getEnumType().getName() : d.getEnumType().getFullName(); > break; > default: > field_type = d.getType().name().toLowerCase(); > } > > final String label; > switch(d.toProto().getLabel()){ > case LABEL_OPTIONAL: > default: > label = "optional"; > break; > case LABEL_REPEATED: > label = "repeated"; > break; > case LABEL_REQUIRED: > label = "required"; > break; > } > > SubstituteAndAppend(contents, "$0$1 $2 $3 = $4", > prefix, > label, > field_type, > d.getType() == > FieldDescriptor.Type.GROUP ? d.getMessageType().getName() : > d.getName(), > d.getNumber()); > > boolean bracketed = false; > if (d.hasDefaultValue()) { > bracketed = true; > SubstituteAndAppend(contents, " [default = $0", > DefaultValueAsString(d, true)); > } > > StringBuilder formatted_options = new StringBuilder(); > if (FormatBracketedOptions(d.getOptions(), formatted_options)) { > contents.append(bracketed ? ", " : " ["); > bracketed = true; > contents.append(formatted_options); > } > > if (bracketed) { > contents.append("]"); > } > > if (d.getType() == FieldDescriptor.Type.GROUP) { > Descriptor_DebugString(d.getMessageType(), depth, contents); > } else { > contents.append(";\n"); > } > } > > static public StringBuilder EnumDescriptor_DebugString(EnumDescriptor > d) { > StringBuilder contents = new StringBuilder(); > EnumDescriptor_DebugString(d, 0, contents); > return contents; > } > > static void EnumDescriptor_DebugString(EnumDescriptor d, int depth, > StringBuilder contents) { > String prefix = prefix(depth * 2, ' '); > ++depth; > SubstituteAndAppend(contents, "$0enum $1 {\n", > prefix, d.getName()); > > FormatLineOptions(depth, d.getOptions(), contents); > > for (EnumValueDescriptor value : d.getValues()) { > EnumValueDescriptor_DebugString(value, depth, contents); > } > SubstituteAndAppend(contents, "$0}\n", prefix); > } > > static public StringBuilder > EnumValueDescriptor_DebugString(EnumValueDescriptor d) { > StringBuilder contents = new StringBuilder(); > EnumValueDescriptor_DebugString(d, 0, contents); > return contents; > } > > static void EnumValueDescriptor_DebugString(EnumValueDescriptor d, > int depth, StringBuilder contents) { > String prefix = prefix(depth * 2, ' '); > SubstituteAndAppend(contents, "$0$1 = $2", > prefix, d.getName(), d.getNumber()); > > StringBuilder formatted_options = new StringBuilder(); > if (FormatBracketedOptions(d.getOptions(), formatted_options)) { > SubstituteAndAppend(contents, " [$0]", formatted_options); > } > contents.append(";\n"); > } > > static public StringBuilder > ServiceDescriptor_DebugString(ServiceDescriptor d) { > StringBuilder contents = new StringBuilder(); > ServiceDescriptor_DebugString(d, contents); > return contents; > } > > static void ServiceDescriptor_DebugString(ServiceDescriptor d, > StringBuilder contents) { > SubstituteAndAppend(contents, "service $0 {\n", d.getName()); > > FormatLineOptions(1, d.getOptions(), contents); > > for (MethodDescriptor method : d.getMethods()) { > MethodDescriptor_DebugString(method, 1, contents); > } > > contents.append("}\n"); > } > > static public StringBuilder > MethodDescriptor_DebugString(MethodDescriptor d) { > StringBuilder contents = new StringBuilder(); > MethodDescriptor_DebugString(d, 0, contents); > return contents; > } > > static void MethodDescriptor_DebugString(MethodDescriptor d, int > depth, StringBuilder contents) { > String prefix = prefix(depth * 2, ' '); > ++depth; > SubstituteAndAppend(contents, "$0rpc $1(.$2) returns (.$3)", > prefix, d.getName(), > d.getInputType().getFullName(), > d.getOutputType().getFullName()); > > StringBuilder formatted_options = new StringBuilder(); > if (FormatLineOptions(depth, d.getOptions(), formatted_options)) { > SubstituteAndAppend(contents, " {\n$0$1}\n", > formatted_options, prefix); > } else { > contents.append(";\n"); > } > } > // > =================================================================== > > > > > > > > } -- You received this message because you are subscribed to the Google Groups "Protocol Buffers" group. To post to this group, send email to protobuf@googlegroups.com. To unsubscribe from this group, send email to protobuf+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/protobuf?hl=en.