Skip to content

Commit 32c2081

Browse files
authored
Merge pull request #86 from salesforce/feature/JDK8-comments
Feature/jdk8 comments
2 parents 72d8f07 + 2154bbc commit 32c2081

File tree

5 files changed

+95
-56
lines changed

5 files changed

+95
-56
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.salesforce.jprotoc;
2+
3+
public class JavaMethodStubTest {
4+
5+
}

jprotoc-test/src/test/proto/helloworld.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ service Greeter {
1616
rpc SayHello (HelloRequest) returns (HelloResponse) {}
1717

1818
// Sends the current time
19-
rpc SayTime (google.protobuf.Empty) returns (TimeResponse) {}
19+
rpc SayTime (google.protobuf.Empty) returns (TimeResponse) {option deprecated=true;}
2020

2121
rpc SomeParam (my.someparameters.SomeParameter) returns (my.someparameters.SomeParameter) {}
2222
}

jprotoc/src/main/java/com/salesforce/jprotoc/jdk8/Jdk8Generator.java

Lines changed: 75 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package com.salesforce.jprotoc.jdk8;
99

1010
import com.google.common.base.Strings;
11+
import com.google.common.html.HtmlEscapers;
1112
import com.google.protobuf.DescriptorProtos;
1213
import com.google.protobuf.compiler.PluginProtos;
1314
import com.salesforce.jprotoc.Generator;
@@ -16,6 +17,7 @@
1617
import com.salesforce.jprotoc.ProtocPlugin;
1718

1819
import java.util.ArrayList;
20+
import java.util.Arrays;
1921
import 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
}

jprotoc/src/main/resources/Jdk8Stub.mustache

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,20 @@ public class {{className}} {
2424
/**
2525
* Creates a new CompletableFuture-style stub that supports unary calls on the service
2626
*/
27+
@SuppressWarnings("unchecked")
2728
public static {{serviceName}}CompletableFutureStub newCompletableFutureStub(Channel channel) {
2829
return newCompletableFutureStub(channel, MoreFutures::toCompletableFuture);
2930
}
3031

3132
/**
3233
* Creates a new CompletableFuture-style stub that supports unary calls on the service
3334
*/
35+
@SuppressWarnings("unchecked")
3436
public static {{serviceName}}CompletableFutureStub newCompletableFutureStub(Channel channel, Function<ListenableFuture, CompletableFuture> futureAdapter) {
3537
return new {{serviceName}}CompletableFutureStub(channel, futureAdapter);
3638
}
3739

40+
{{#javaDoc}}{{{javaDoc}}}{{/javaDoc}}
3841
public static final class {{serviceName}}CompletableFutureStub extends AbstractStub<{{serviceName}}CompletableFutureStub> {
3942
private {{serviceName}}Grpc.{{serviceName}}FutureStub innerStub;
4043
private Function<ListenableFuture, CompletableFuture> futureAdapter;
@@ -48,39 +51,26 @@ public class {{className}} {
4851
private {{serviceName}}CompletableFutureStub(Channel channel, CallOptions callOptions, Function<ListenableFuture, CompletableFuture> futureAdapter) {
4952
super(channel, callOptions);
5053
this.futureAdapter = futureAdapter;
51-
innerStub = {{serviceName}}Grpc.newFutureStub(channel);
54+
innerStub = {{serviceName}}Grpc.newFutureStub(channel).build(channel, callOptions);
5255
}
5356

5457
@Override
5558
protected {{serviceName}}CompletableFutureStub build(Channel channel, CallOptions callOptions) {
5659
{{serviceName}}CompletableFutureStub stub = new {{serviceName}}CompletableFutureStub(channel, callOptions, futureAdapter);
57-
forceCallOptions(stub.innerStub, callOptions);
5860
return stub;
5961
}
60-
{{#methods}}{{#deprecated}}@Deprecated{{/deprecated}}
62+
{{#methods}}
63+
64+
{{#javaDoc}}
65+
{{{javaDoc}}}
66+
{{/javaDoc}}
67+
{{#deprecated}}
68+
@java.lang.Deprecated
69+
{{/deprecated}}
70+
@SuppressWarnings("unchecked")
6171
public CompletableFuture<{{outputType}}> {{methodName}}({{inputType}} request) {
6272
return futureAdapter.apply(innerStub.{{methodName}}(request));
6373
}
6474
{{/methods}}
65-
66-
private void forceCallOptions({{serviceName}}Grpc.{{serviceName}}FutureStub stub, CallOptions options) {
67-
try {
68-
callOptionsField.set(stub, options);
69-
} catch (IllegalAccessException e) {
70-
throw new RuntimeException(e);
71-
}
72-
}
73-
}
74-
75-
private static final Field callOptionsField = getWritableCallOptonsField();
76-
77-
private static Field getWritableCallOptonsField() {
78-
try {
79-
Field callOptionsField = AbstractStub.class.getDeclaredField("callOptions");
80-
callOptionsField.setAccessible(true);
81-
return callOptionsField;
82-
} catch (NoSuchFieldException e) {
83-
throw new RuntimeException(e);
84-
}
8575
}
8676
}

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@
226226
<configuration>
227227
<source>${maven.compiler.source}</source>
228228
<target>${maven.compiler.target}</target>
229+
<compilerArgument>-Xlint:unchecked</compilerArgument>
229230
</configuration>
230231
</plugin>
231232

0 commit comments

Comments
 (0)