1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.google.api.generator.gapic.composer.comment;
16 
17 import com.google.api.generator.engine.ast.CommentStatement;
18 import com.google.api.generator.engine.ast.JavaDocComment;
19 import com.google.api.generator.engine.ast.TypeNode;
20 import com.google.api.generator.gapic.utils.JavaStyle;
21 import com.google.common.base.Preconditions;
22 import java.util.Arrays;
23 import java.util.List;
24 import java.util.Optional;
25 import java.util.stream.Collectors;
26 
27 public class SettingsCommentComposer {
28   private static final String COLON = ":";
29 
30   private static final String BUILDER_CLASS_DOC_PATTERN = "Builder for %s.";
31   private static final String CALL_SETTINGS_METHOD_DOC_PATTERN =
32       "Returns the object with the settings used for calls to %s.";
33   private static final String CALL_SETTINGS_BUILDER_METHOD_DOC_PATTERN =
34       "Returns the builder for the settings used for calls to %s.";
35 
36   // Class header patterns.
37   private static final String CLASS_HEADER_SUMMARY_PATTERN =
38       "Settings class to configure an instance of {@link %s}.";
39   private static final String CLASS_HEADER_DEFAULT_ADDRESS_PORT_PATTERN =
40       "The default service address (%s) and default port (%d) are used.";
41   private static final String CLASS_HEADER_SAMPLE_CODE_PATTERN =
42       "For example, to set the total timeout of %s to 30 seconds:";
43 
44   private static final String CLASS_HEADER_BUILDER_DESCRIPTION =
45       "The builder of this class is recursive, so contained classes are themselves builders. When"
46           + " build() is called, the tree of builders is called to create the complete settings"
47           + " object.";
48   private static final String CLASS_HEADER_DEFAULTS_DESCRIPTION =
49       "The default instance has everything set to sensible defaults:";
50   private static final String CLASS_HEADER_DEFAULTS_CREDENTIALS_DESCRIPTION =
51       "Credentials are acquired automatically through Application Default Credentials.";
52   private static final String CLASS_HEADER_DEFAULTS_RETRIES_DESCRIPTION =
53       "Retries are configured for idempotent methods but not for non-idempotent methods.";
54 
55   public static final CommentStatement DEFAULT_SCOPES_COMMENT =
56       toSimpleComment("The default scopes of the service.");
57 
58   public static final CommentStatement DEFAULT_EXECUTOR_PROVIDER_BUILDER_METHOD_COMMENT =
59       toSimpleComment("Returns a builder for the default ExecutorProvider for this service.");
60   public static final CommentStatement DEFAULT_SERVICE_ENDPOINT_METHOD_COMMENT =
61       toSimpleComment("Returns the default service endpoint.");
62   public static final CommentStatement DEFAULT_SERVICE_MTLS_ENDPOINT_METHOD_COMMENT =
63       toSimpleComment("Returns the default mTLS service endpoint.");
64   public static final CommentStatement DEFAULT_SERVICE_SCOPES_METHOD_COMMENT =
65       toSimpleComment("Returns the default service scopes.");
66 
67   public static final CommentStatement DEFAULT_CREDENTIALS_PROVIDER_BUILDER_METHOD_COMMENT =
68       toSimpleComment("Returns a builder for the default credentials for this service.");
69 
70   public static final CommentStatement DEFAULT_TRANSPORT_PROVIDER_BUILDER_METHOD_COMMENT =
71       toSimpleComment("Returns a builder for the default ChannelProvider for this service.");
72 
73   public static final CommentStatement NEW_BUILDER_METHOD_COMMENT =
74       toSimpleComment("Returns a new builder for this class.");
75 
76   public static final CommentStatement TO_BUILDER_METHOD_COMMENT =
77       toSimpleComment("Returns a builder containing all the values of this settings class.");
78 
79   public static final List<CommentStatement> APPLY_TO_ALL_UNARY_METHODS_METHOD_COMMENTS =
80       Arrays.asList(
81               JavaDocComment.builder()
82                   .addComment(
83                       "Applies the given settings updater function to all of the unary API methods"
84                           + " in this service.")
85                   .addParagraph(
86                       "Note: This method does not support applying settings to streaming methods.")
87                   .build())
88           .stream()
89           .map(c -> CommentStatement.withComment(c))
90           .collect(Collectors.toList());
91 
92   private final CommentStatement newTransportBuilderMethodComment;
93   private final CommentStatement transportProviderBuilderMethodComment;
94 
SettingsCommentComposer(String transportPrefix)95   public SettingsCommentComposer(String transportPrefix) {
96     this.newTransportBuilderMethodComment =
97         toSimpleComment(String.format("Returns a new %s builder for this class.", transportPrefix));
98     this.transportProviderBuilderMethodComment =
99         toSimpleComment(
100             String.format(
101                 "Returns a builder for the default %s ChannelProvider for this service.",
102                 transportPrefix));
103   }
104 
getNewTransportBuilderMethodComment()105   public CommentStatement getNewTransportBuilderMethodComment() {
106     return newTransportBuilderMethodComment;
107   }
108 
getTransportProviderBuilderMethodComment()109   public CommentStatement getTransportProviderBuilderMethodComment() {
110     return transportProviderBuilderMethodComment;
111   }
112 
createCallSettingsGetterComment( String javaMethodName, boolean isMethodDeprecated)113   public static CommentStatement createCallSettingsGetterComment(
114       String javaMethodName, boolean isMethodDeprecated) {
115     String methodComment = String.format(CALL_SETTINGS_METHOD_DOC_PATTERN, javaMethodName);
116     return isMethodDeprecated
117         ? toDeprecatedSimpleComment(methodComment)
118         : toSimpleComment(methodComment);
119   }
120 
createBuilderClassComment(String outerClassName)121   public static CommentStatement createBuilderClassComment(String outerClassName) {
122     return toSimpleComment(String.format(BUILDER_CLASS_DOC_PATTERN, outerClassName));
123   }
124 
createCallSettingsBuilderGetterComment( String javaMethodName, boolean isMethodDeprecated)125   public static CommentStatement createCallSettingsBuilderGetterComment(
126       String javaMethodName, boolean isMethodDeprecated) {
127     String methodComment = String.format(CALL_SETTINGS_BUILDER_METHOD_DOC_PATTERN, javaMethodName);
128     return isMethodDeprecated
129         ? toDeprecatedSimpleComment(methodComment)
130         : toSimpleComment(methodComment);
131   }
132 
createClassHeaderComments( String configuredClassName, String defaultHost, boolean isDeprecated, Optional<String> methodNameOpt, Optional<String> sampleCodeOpt, TypeNode classType)133   public static List<CommentStatement> createClassHeaderComments(
134       String configuredClassName,
135       String defaultHost,
136       boolean isDeprecated,
137       Optional<String> methodNameOpt,
138       Optional<String> sampleCodeOpt,
139       TypeNode classType) {
140     // Split default address and port.
141     int colonIndex = defaultHost.indexOf(COLON);
142     Preconditions.checkState(
143         colonIndex > 0 && colonIndex < defaultHost.length() - 1,
144         String.format(
145             "No valid address and port found for %s, expected a string formatted like"
146                 + " localhost:8888",
147             defaultHost));
148     String defaultAddress = defaultHost.substring(0, colonIndex);
149     int defaultPort = Integer.parseInt(defaultHost.substring(colonIndex + 1));
150 
151     JavaDocComment.Builder javaDocCommentBuilder =
152         JavaDocComment.builder()
153             .addUnescapedComment(String.format(CLASS_HEADER_SUMMARY_PATTERN, configuredClassName))
154             .addParagraph(CLASS_HEADER_DEFAULTS_DESCRIPTION)
155             .addUnorderedList(
156                 Arrays.asList(
157                     String.format(
158                         CLASS_HEADER_DEFAULT_ADDRESS_PORT_PATTERN, defaultAddress, defaultPort),
159                     CLASS_HEADER_DEFAULTS_CREDENTIALS_DESCRIPTION,
160                     CLASS_HEADER_DEFAULTS_RETRIES_DESCRIPTION))
161             .addParagraph(CLASS_HEADER_BUILDER_DESCRIPTION);
162 
163     if (methodNameOpt.isPresent() && sampleCodeOpt.isPresent()) {
164       javaDocCommentBuilder =
165           javaDocCommentBuilder
166               .addParagraph(
167                   String.format(
168                       CLASS_HEADER_SAMPLE_CODE_PATTERN,
169                       JavaStyle.toLowerCamelCase(methodNameOpt.get())))
170               .addSampleCode(sampleCodeOpt.get());
171     }
172 
173     if (isDeprecated) {
174       javaDocCommentBuilder.setDeprecated(CommentComposer.DEPRECATED_CLASS_STRING);
175     }
176 
177     return Arrays.asList(
178         CommentComposer.AUTO_GENERATED_CLASS_COMMENT,
179         CommentStatement.withComment(javaDocCommentBuilder.build()));
180   }
181 
toSimpleComment(String comment)182   private static CommentStatement toSimpleComment(String comment) {
183     return CommentStatement.withComment(JavaDocComment.withComment(comment));
184   }
185 
toDeprecatedSimpleComment(String comment)186   private static CommentStatement toDeprecatedSimpleComment(String comment) {
187     return CommentStatement.withComment(
188         JavaDocComment.builder()
189             .addComment(comment)
190             .setDeprecated(CommentComposer.DEPRECATED_METHOD_STRING)
191             .build());
192   }
193 }
194