88package com .salesforce .jprotoc .jdk8 ;
99
1010import com .google .common .base .Strings ;
11+ import com .google .common .html .HtmlEscapers ;
1112import com .google .protobuf .DescriptorProtos ;
1213import com .google .protobuf .compiler .PluginProtos ;
1314import com .salesforce .jprotoc .Generator ;
1617import com .salesforce .jprotoc .ProtocPlugin ;
1718
1819import java .util .ArrayList ;
20+ import java .util .Arrays ;
1921import java .util .List ;
2022
2123/**
@@ -28,14 +30,17 @@ public static void main(String[] args) {
2830
2931 private static final String CLASS_SUFFIX = "Grpc8" ;
3032
33+ private static final String SERVICE_JAVADOC_PREFIX = " " ;
34+ private static final String METHOD_JAVADOC_PREFIX = " " ;
35+
3136 @ Override
3237 public List <PluginProtos .CodeGeneratorResponse .File > generateFiles (PluginProtos .CodeGeneratorRequest request ) throws GeneratorException {
3338 final ProtoTypeMap protoTypeMap = ProtoTypeMap .of (request .getProtoFileList ());
3439 List <PluginProtos .CodeGeneratorResponse .File > files = new ArrayList <>();
3540
3641 for (DescriptorProtos .FileDescriptorProto protoFile : request .getProtoFileList ()) {
3742 if (request .getFileToGenerateList ().contains (protoFile .getName ())) {
38- for (Context ctx : extractContext (protoTypeMap , protoFile )) {
43+ for (ServiceContext ctx : extractContext (protoTypeMap , protoFile )) {
3944 files .add (buildFile (ctx ));
4045 }
4146 }
@@ -44,17 +49,23 @@ public List<PluginProtos.CodeGeneratorResponse.File> generateFiles(PluginProtos.
4449 return files ;
4550 }
4651
47- private List <Context > extractContext (ProtoTypeMap protoTypeMap , DescriptorProtos .FileDescriptorProto proto ) {
48- List <Context > contexts = new ArrayList <>();
49-
50- for (DescriptorProtos .ServiceDescriptorProto service : proto .getServiceList ()) {
51- Context ctx = extractServiceContext (protoTypeMap , service );
52- ctx .packageName = extractPackageName (proto );
53- ctx .protoName = proto .getName ();
54- contexts .add (ctx );
55- }
52+ private List <ServiceContext > extractContext (ProtoTypeMap protoTypeMap , DescriptorProtos .FileDescriptorProto fileProto ) {
53+ List <ServiceContext > serviceContexts = new ArrayList <>();
54+
55+ List <DescriptorProtos .SourceCodeInfo .Location > locations = fileProto .getSourceCodeInfo ().getLocationList ();
56+ locations .stream ()
57+ .filter (location -> location .getPathCount () == 2 && location .getPath (0 ) == DescriptorProtos .FileDescriptorProto .SERVICE_FIELD_NUMBER )
58+ .forEach (location -> {
59+ int serviceNumber = location .getPath (1 );
60+ DescriptorProtos .ServiceDescriptorProto serviceProto = fileProto .getService (serviceNumber );
61+ ServiceContext ctx = extractServiceContext (protoTypeMap , serviceProto , locations , serviceNumber );
62+ ctx .packageName = extractPackageName (fileProto );
63+ ctx .protoName = fileProto .getName ();
64+ ctx .javaDoc = getJavaDoc (getComments (location ), SERVICE_JAVADOC_PREFIX );
65+ serviceContexts .add (ctx );
66+ });
5667
57- return contexts ;
68+ return serviceContexts ;
5869 }
5970
6071 private String extractPackageName (DescriptorProtos .FileDescriptorProto proto ) {
@@ -69,26 +80,38 @@ private String extractPackageName(DescriptorProtos.FileDescriptorProto proto) {
6980 return Strings .nullToEmpty (proto .getPackage ());
7081 }
7182
72- private Context extractServiceContext (
83+ private static final int METHOD_NUMBER_OF_PATHS = 4 ;
84+ private ServiceContext extractServiceContext (
7385 ProtoTypeMap protoTypeMap ,
74- DescriptorProtos .ServiceDescriptorProto serviceProto ) {
75- Context ctx = new Context ();
86+ DescriptorProtos .ServiceDescriptorProto serviceProto ,
87+ List <DescriptorProtos .SourceCodeInfo .Location > locations ,
88+ int serviceNumber ) {
89+ ServiceContext ctx = new ServiceContext ();
7690 ctx .fileName = serviceProto .getName () + CLASS_SUFFIX + ".java" ;
7791 ctx .className = serviceProto .getName () + CLASS_SUFFIX ;
7892 ctx .serviceName = serviceProto .getName ();
7993 ctx .deprecated = serviceProto .getOptions () != null && serviceProto .getOptions ().getDeprecated ();
8094
81- // Identify methods to generate a CompletableFuture-based client for.
82- // Only unary methods are supported.
83- serviceProto .getMethodList ().stream ()
84- .filter (method -> !method .getClientStreaming () && !method .getServerStreaming ())
85- .forEach (method -> {
86- ContextMethod ctxMethod = new ContextMethod ();
87- ctxMethod .methodName = lowerCaseFirst (method .getName ());
88- ctxMethod .inputType = protoTypeMap .toJavaTypeName (method .getInputType ());
89- ctxMethod .outputType = protoTypeMap .toJavaTypeName (method .getOutputType ());
90- ctxMethod .deprecated = method .getOptions () != null && method .getOptions ().getDeprecated ();
91- ctx .methods .add (ctxMethod );
95+ locations .stream ()
96+ .filter (location -> location .getPathCount () == METHOD_NUMBER_OF_PATHS &&
97+ location .getPath (0 ) == DescriptorProtos .FileDescriptorProto .SERVICE_FIELD_NUMBER &&
98+ location .getPath (1 ) == serviceNumber &&
99+ location .getPath (2 ) == DescriptorProtos .ServiceDescriptorProto .METHOD_FIELD_NUMBER )
100+ .forEach (location -> {
101+ int methodNumber = location .getPath (METHOD_NUMBER_OF_PATHS - 1 );
102+ DescriptorProtos .MethodDescriptorProto methodProto = serviceProto .getMethod (methodNumber );
103+
104+ // Identify methods to generate a CompletableFuture-based client for.
105+ // Only unary methods are supported.
106+ if (!methodProto .getClientStreaming () && !methodProto .getServerStreaming ()) {
107+ MethodContext ctxMethod = new MethodContext ();
108+ ctxMethod .methodName = lowerCaseFirst (methodProto .getName ());
109+ ctxMethod .inputType = protoTypeMap .toJavaTypeName (methodProto .getInputType ());
110+ ctxMethod .outputType = protoTypeMap .toJavaTypeName (methodProto .getOutputType ());
111+ ctxMethod .deprecated = methodProto .getOptions () != null && methodProto .getOptions ().getDeprecated ();
112+ ctxMethod .javaDoc = getJavaDoc (getComments (location ), METHOD_JAVADOC_PREFIX );
113+ ctx .methods .add (ctxMethod );
114+ }
92115 });
93116 return ctx ;
94117 }
@@ -97,7 +120,7 @@ private String lowerCaseFirst(String s) {
97120 return Character .toLowerCase (s .charAt (0 )) + s .substring (1 );
98121 }
99122
100- private String absoluteFileName (Context ctx ) {
123+ private String absoluteFileName (ServiceContext ctx ) {
101124 String dir = ctx .packageName .replace ('.' , '/' );
102125 if (Strings .isNullOrEmpty (dir )) {
103126 return ctx .fileName ;
@@ -106,7 +129,7 @@ private String absoluteFileName(Context ctx) {
106129 }
107130 }
108131
109- private PluginProtos .CodeGeneratorResponse .File buildFile (Context context ) {
132+ private PluginProtos .CodeGeneratorResponse .File buildFile (ServiceContext context ) {
110133 String content = applyTemplate ("Jdk8Stub.mustache" , context );
111134 return PluginProtos .CodeGeneratorResponse .File
112135 .newBuilder ()
@@ -115,28 +138,48 @@ private PluginProtos.CodeGeneratorResponse.File buildFile(Context context) {
115138 .build ();
116139 }
117140
141+ private String getComments (DescriptorProtos .SourceCodeInfo .Location location ) {
142+ return location .getLeadingComments ().isEmpty () ? location .getTrailingComments () : location .getLeadingComments ();
143+ }
144+
145+ private String getJavaDoc (String comments , String prefix ) {
146+ if (!comments .isEmpty ()) {
147+ StringBuilder builder = new StringBuilder ("/**\n " )
148+ .append (prefix ).append (" * <pre>\n " );
149+ Arrays .stream (HtmlEscapers .htmlEscaper ().escape (comments ).split ("\n " ))
150+ .forEach (line -> builder .append (prefix ).append (" * " ).append (line ).append ("\n " ));
151+ builder
152+ .append (prefix ).append (" * <pre>\n " )
153+ .append (prefix ).append (" */" );
154+ return builder .toString ();
155+ }
156+ return null ;
157+ }
158+
118159 /**
119160 * Backing class for mustache template.
120161 */
121- private class Context {
122- // CHECKSTYLE DISABLE VisibilityModifier FOR 7 LINES
162+ private class ServiceContext {
163+ // CHECKSTYLE DISABLE VisibilityModifier FOR 8 LINES
123164 public String fileName ;
124165 public String protoName ;
125166 public String packageName ;
126167 public String className ;
127168 public String serviceName ;
128169 public boolean deprecated ;
129- public final List <ContextMethod > methods = new ArrayList <>();
170+ public String javaDoc ;
171+ public final List <MethodContext > methods = new ArrayList <>();
130172 }
131173
132174 /**
133175 * Backing class for mustache template.
134176 */
135- private class ContextMethod {
136- // CHECKSTYLE DISABLE VisibilityModifier FOR 4 LINES
177+ private class MethodContext {
178+ // CHECKSTYLE DISABLE VisibilityModifier FOR 5 LINES
137179 public String methodName ;
138180 public String inputType ;
139181 public String outputType ;
140182 public boolean deprecated ;
183+ public String javaDoc ;
141184 }
142185}
0 commit comments