Skip to content

Commit fbb202a

Browse files
committed
javadoc changes
1 parent 8237d31 commit fbb202a

File tree

2 files changed

+269
-1
lines changed

2 files changed

+269
-1
lines changed

mug-guava/src/main/java/com/google/mu/safesql/SafeSql.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,23 @@
265265
* List<Long> ids = sql.query(connection, row -> row.getLong("id"));
266266
* }</pre>
267267
*
268+
* <p><strong>Automatic Escaping: No Need for ESCAPE Clause</strong></p>
269+
*
270+
* <p>This means you <em>do not</em> need to (and in fact, must not) write SQL
271+
* using {@code ESCAPE} clauses. Any such attempt, like:
272+
*
273+
* <pre>{@code
274+
* SELECT name FROM Users WHERE name LIKE '%{term}%' ESCAPE '\'
275+
* }</pre>
276+
*
277+
* <p>...will be rejected, because SafeSql already performs all necessary escaping internally.
278+
* This eliminates the need for developers to deal with brittle double-escaping
279+
* (like {@code '\\'}), improves readability, and avoids cross-database compatibility issues.
280+
*
281+
* <p>If you find yourself wanting to use {@code ESCAPE}, consider whether you are
282+
* manually escaping strings that could instead be safely passed as-is to SafeSql's
283+
* template system.
284+
*
268285
* <dl><dt><STRONG>Quote String Placeholders</STRONG></dt></dl>
269286
*
270287
* Even when you don't use the {@code LIKE} operator or the percent sign (%), it may still be
@@ -924,7 +941,8 @@ public static <T> Template<List<T>> prepareToQuery(
924941
* <p>The template arguments follow the same rules as discussed in {@link #of(String, Object...)}
925942
* and receives the same compile-time protection against mismatch or out-of-order human mistakes.
926943
*
927-
* <p>The returned Template is <em>not</em> thread safe.
944+
* <p>The returned Template is <em>not</em> thread safe because the cached {@link
945+
* PreparedStatement} objects aren't.
928946
*
929947
* <p>The caller is expected to close the {@code connection} after done, which will close the
930948
* cached PreparedStatement.

mug/src/main/java/com/google/mu/util/Substring.java

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,256 @@
115115
* assertThat(rendered).isEqualTo("Arya Stark went to Braavos.");
116116
* }</pre>
117117
*
118+
* <h2>From Apache StringUtils to Substring</h2>
119+
* <table border="1" cellpadding="6" cellspacing="0">
120+
* <thead><tr><th>StringUtils Style</th><th>Substring Style</th></tr></thead>
121+
*
122+
* <tr>
123+
* <td><pre>{@code
124+
* // Success
125+
* String username = substringBefore("user@gmail.com", "@");
126+
* // => "user"
127+
*
128+
* // '.' not found in "readme", returns empty
129+
* String filename = substringBefore("readme", ".");
130+
* // => ""
131+
* }</pre></td>
132+
* <td><pre>{@code
133+
* // Success
134+
* String username = before(first("@"))
135+
* .from("user@gmail.com");
136+
* // => "user"
137+
*
138+
* // explicitly specify "readme" as fallback
139+
* String filename = before(first("."))
140+
* .from("readme")
141+
* .orElse("readme");
142+
* // => "readme"
143+
* }</pre></td>
144+
* </tr>
145+
*
146+
* <tr>
147+
* <td><pre>{@code
148+
* // Success
149+
* String emailDomain = substringAfter("user@gmail.com", "@");
150+
* // => "gmail.com"
151+
*
152+
* // '.' not found in "myfile": full name is assumed to be extension
153+
* String extension = substringAfter("myfile", ".");
154+
* // => "myfile"
155+
* }</pre></td>
156+
* <td><pre>{@code
157+
* // Success
158+
* String emailDomain = after(first("@"))
159+
* .from("user@gmail.com");
160+
* // => "gmail.com"
161+
*
162+
* // explicitly specify "n/a" as fallback
163+
* String extension = after(first("."))
164+
* .from("myfile")
165+
* .orElse("n/a");
166+
* // => "n/a"
167+
* }</pre></td>
168+
* </tr>
169+
*
170+
* <tr>
171+
* <td><pre>{@code
172+
* // Success
173+
* String extension = substringAfterLast("myfile.txt", ".");
174+
* // => "txt"
175+
*
176+
* // '.' not found in "myfile": full name is assumed to be extension
177+
* String extension = substringAfterLast("myfile", ".");
178+
* // => "myfile"
179+
* }</pre></td>
180+
* <td><pre>{@code
181+
* // Success
182+
* String extension = after(last("."))
183+
* .from("myfile.txt");
184+
* // => "txt"
185+
*
186+
* // explicitly specify "n/a" as fallback
187+
* String extension = after(last("."))
188+
* .from("myfile")
189+
* .orElse("n/a");
190+
* // => "n/a"
191+
* }</pre></td>
192+
* </tr>
193+
*
194+
* <tr>
195+
* <td><pre>{@code
196+
* // Success
197+
* String value = substringBetween("<a>hello</a>", "<a>", "</a>");
198+
* // => "hello"
199+
*
200+
* // Tags not matched in "value": returns null
201+
* String fallback = substringBetween("value", "<a>", "</a>");
202+
* // => null
203+
* }</pre></td>
204+
* <td><pre>{@code
205+
* // Success
206+
* String value = between("<a>", "</a>")
207+
* .from("<a>hello</a>");
208+
* // => "hello"
209+
*
210+
* // explicitly specify "n/a" as fallback
211+
* String fallback = between("<a>", "</a>")
212+
* .from("value")
213+
* .orElse("n/a");
214+
* // => "n/a"
215+
* }</pre></td>
216+
* </tr>
217+
*
218+
* <tr>
219+
* <td><pre>{@code
220+
* // Success
221+
* String[] values = substringsBetween("<a>1</a><a>2</a>", "<a>", "</a>");
222+
* // => ["1", "2"]
223+
*
224+
* // Tag mismatch: returns null
225+
* String[] values = substringsBetween("value", "<a>", "</a>");
226+
* // => null
227+
* }</pre></td>
228+
* <td><pre>{@code
229+
* // Returns [] if not found
230+
* List<String> values = between("<a>", "</a>")
231+
* .repeatedly()
232+
* .from("<a>1</a><a>2</a>")
233+
* .toList();
234+
* // => ["1", "2"]
235+
* }</pre></td>
236+
* </tr>
237+
*
238+
* <tr>
239+
* <td><pre>{@code
240+
* String result = replacePattern("v1.2.3", "\\d+", "x");
241+
* // => "vx.x.x"
242+
* }</pre></td>
243+
* <td><pre>{@code
244+
* // Always use repeatedly() to apply to all occurrences
245+
* String result = first(Pattern.compile("\\d+"))
246+
* .repeatedly()
247+
* .replaceAllFrom("v1.2.3", m -> "x");
248+
* // => "vx.x.x"
249+
* }</pre></td>
250+
* </tr>
251+
*
252+
* <tr>
253+
* <td><pre>{@code
254+
* String result = replaceEachRepeatedly("a-b-a", new String[]{"a", "b"}, new String[]{"x", "y"});
255+
* // => "x-y-x"
256+
* }</pre></td>
257+
* <td><pre>{@code
258+
* // Always use repeatedly() to apply to all occurrences
259+
* Map<String, String> replacements = Map.of("a", "x", "b", "y");
260+
* String result = replacements.keySet().stream()
261+
* .map(Substring::first)
262+
* .collect(firstOccurrence())
263+
* .replaceAllFrom("a-b-a", m -> replacements.get(m.toString()));
264+
* // => "x-y-x"
265+
* }</pre></td>
266+
* </tr>
267+
*
268+
* <tr>
269+
* <td><pre>{@code
270+
* String name = removeStart("Mr.Bond", "Mr.");
271+
* // => "Bond"
272+
* }</pre></td>
273+
* <td><pre>{@code
274+
* String name = prefix("Mr.").removeFrom("Mr.Bond");
275+
* // => "Bond"
276+
* }</pre></td>
277+
* </tr>
278+
*
279+
* <tr>
280+
* <td><pre>{@code
281+
* String name = removeEnd("file.txt", ".txt");
282+
* // => "file"
283+
* }</pre></td>
284+
* <td><pre>{@code
285+
* String name = suffix(".txt").removeFrom("file.txt");
286+
* // => "file"
287+
* }</pre></td>
288+
* </tr>
289+
*
290+
* <tr>
291+
* <td><pre>{@code
292+
* // Filters out empty
293+
* String[] parts = split("a,b,c,", ",");
294+
* // => ["a", "b", "c"]
295+
* }</pre></td>
296+
* <td><pre>{@code
297+
* // Explicitly filter out empty matches
298+
* List<String> parts = all(",")
299+
* .split("a,b,c,")
300+
* .filter(Match::isNotEmpty)
301+
* .map(Match::toString)
302+
* .toList();
303+
* // => ["a", "b", "c"]
304+
* }</pre></td>
305+
* </tr>
306+
*
307+
* <tr>
308+
* <td><pre>{@code
309+
* // Retains empty
310+
* String[] parts = splitPreserveAllTokens("a,,b", ",");
311+
* // => ["a", "", "b"]
312+
* }</pre></td>
313+
* <td><pre>{@code
314+
* List<String> parts = all(",")
315+
* .split("a,,b")
316+
* .map(Match::toString)
317+
* .toList();
318+
* // => ["a", "", "b"]
319+
* }</pre></td>
320+
* </tr>
321+
*
322+
* <tr>
323+
* <td><pre>{@code
324+
* // By -, --, --- etc. Ignores empty.
325+
* String[] parts = splitByWholeSeparator("a--b-c-", "-");
326+
* // => ["a", "b", "c"]
327+
* }</pre></td>
328+
* <td><pre>{@code
329+
* // Explicitly filter out empty matches
330+
* List<String> parts = consecutive(is('-'))
331+
* .repeatedly()
332+
* .split("a--b-c-")
333+
* .filter(Match::isNotEmpty)
334+
* .map(Match::toString)
335+
* .toList();
336+
* // => ["a", "b", "c"]
337+
* }</pre></td>
338+
* </tr>
339+
*
340+
* <tr>
341+
* <td><pre>{@code
342+
* // By -, --, --- etc. Retains empty.
343+
* String[] parts = splitByWholeSeparatorPreserveAllTokens("a--", "-");
344+
* // => ["a", ""]
345+
* }</pre></td>
346+
* <td><pre>{@code
347+
* List<String> parts = consecutive(is('-'))
348+
* .repeatedly()
349+
* .split("a--")
350+
* .map(Match::toString)
351+
* .toList();
352+
* // => ["a", ""]
353+
* }</pre></td>
354+
* </tr>
355+
*
356+
* <tr>
357+
* <td><pre>{@code
358+
* int count = countMatches("a,b,c", ",");
359+
* // => 2
360+
* }</pre></td>
361+
* <td><pre>{@code
362+
* long count = all(",").match("a,b,c").count();
363+
* // => 2
364+
* }</pre></td>
365+
* </tr>
366+
* </table>
367+
*
118368
* @since 2.0
119369
*/
120370
public final class Substring {

0 commit comments

Comments
 (0)