1+ /*
2+ * Copyright 2025 Vonage
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
123import java .io .File ;
224import java .io .IOException ;
325import java .nio .file .Files ;
426import java .nio .file .Path ;
527import java .nio .file .Paths ;
628import java .nio .file .StandardOpenOption ;
7- import java .util .Objects ;
29+ import java .util .*;
30+
31+ public final class AggregateSnippets {
832
9- public class AggregateSnippets {
10- public static void main (String [] args ) throws Throwable {
33+ public static void main (String ... args ) throws Throwable {
1134 final var repoRoot = Paths .get ("" ).toAbsolutePath ();
1235 final var snippetsSrcRoot = repoRoot .resolve ("src/main/java/com/vonage/quickstart" );
13- final var classFileName = "AggregateSnippets.java" ;
14- var sb = new StringBuilder (1 << 17 )
36+ final var aggregator = new AggregateSnippets (snippetsSrcRoot );
37+ aggregator .computeContents ();
38+ aggregator .saveContentsToFile (repoRoot .resolve ("SNIPPETS.md" ));
39+ if (args .length > 0 ) {
40+ aggregator .saveLineNumbersToCsv (Paths .get (args [0 ]));
41+ }
42+ }
43+
44+
45+ public record CodeSnippetFileInfo (
46+ Path file ,
47+ int mainStartIndex , int mainEndIndex ,
48+ int clientStartIndex , int clientEndIndex
49+ ) { }
50+
51+ private StringBuilder sb ;
52+ private final Path snippetsSrcRoot ;
53+ private Collection <CodeSnippetFileInfo > snippetFiles ;
54+
55+ public AggregateSnippets (Path snippetsSrcRoot ) {
56+ this .snippetsSrcRoot = Objects .requireNonNull (snippetsSrcRoot );
57+ }
58+
59+ private void checkComputed () {
60+ if (sb == null || sb .isEmpty ()) {
61+ throw new IllegalStateException ("Contents not computed yet." );
62+ }
63+ }
64+
65+ public Collection <CodeSnippetFileInfo > getLineNumbers () {
66+ checkComputed ();
67+ return snippetFiles ;
68+ }
69+
70+ public String getContents () {
71+ checkComputed ();
72+ return sb .toString ();
73+ }
74+
75+ public void saveContentsToFile (Path destPath ) throws IOException {
76+ Files .writeString (destPath , getContents (), StandardOpenOption .CREATE );
77+ }
78+
79+ public void saveLineNumbersToCsv (Path destPath ) throws IOException {
80+ checkComputed ();
81+ try (var writer = Files .newBufferedWriter (destPath , StandardOpenOption .CREATE )) {
82+ writer .write ("File,MainStart,MainEnd,ClientStart,ClientEnd\n " );
83+ for (var metadata : snippetFiles ) {
84+ writer .write (
85+ metadata .file .getFileName () + "," +
86+ metadata .mainStartIndex + "," +
87+ metadata .mainEndIndex + "," +
88+ metadata .clientStartIndex + "," +
89+ metadata .clientEndIndex + "\n "
90+ );
91+ }
92+ }
93+ }
94+
95+ public void computeContents () throws IOException {
96+ snippetFiles = new ArrayList <>(256 );
97+ final String classFileName = getClass ().getSimpleName () + ".java" ;
98+ sb = new StringBuilder (1 << 17 )
1599 .append ("# Vonage Java SDK Code Snippets\n " )
16100 .append ("Here are all the snippets in this repository.\n " )
17101 .append ("This file was generated by running [" ).append (classFileName )
18102 .append ("](src/main/java/" ).append (classFileName )
19103 .append (") from the root of the repository." )
20104 .append ("\n \n ## Contents" );
21105
22- var allDirs = Files .list (snippetsSrcRoot )
23- .filter (Files ::isDirectory )
106+ var allDirs = getAllDirsSorted ();
107+
108+ for (var file : allDirs ) {
109+ var title = toHeadingTitle (file .getName ());
110+ sb .append ("\n - [**" ).append (title ).append ("**](#" )
111+ .append (title .toLowerCase ().replace (' ' , '-' )).append (")" );
112+ }
113+ sb .append ("\n \n " );
114+
115+ for (var file : allDirs ) {
116+ appendSnippetContent (file , 2 );
117+ }
118+ }
119+
120+ private List <File > getAllDirsSorted () {
121+ try (var pathStream = Files .list (snippetsSrcRoot )) {
122+ return pathStream .filter (Files ::isDirectory )
24123 .map (Path ::toFile )
25124 .sorted ((f1 , f2 ) -> {
26125 if (isInitialize (f1 )) return -1 ;
27126 if (isInitialize (f2 )) return 1 ;
28127 return f1 .getName ().compareToIgnoreCase (f2 .getName ());
29128 })
30129 .toList ();
130+ }
131+ catch (IOException ex ) {
132+ System .err .println ("Could not read " +snippetsSrcRoot +": " + ex .getMessage ());
133+ return List .of ();
134+ }
135+ }
31136
32- for (var file : allDirs ) {
33- var title = toHeadingTitle (file .getName ());
34- sb .append ("\n - [**" ).append (title ).append ("**](#" )
35- .append (title .toLowerCase ().replace (' ' , '-' )).append (")" );
137+ private void appendSnippetContent (File path , int level ) throws IOException {
138+ var fileName = path .getName ();
139+ if (path .isFile ()) {
140+ fileName = fileName .substring (0 , fileName .lastIndexOf ('.' ));
141+ }
142+ if (fileName .trim ().length () < 3 ) return ;
143+
144+ sb .append ("#" .repeat (level )).append (' ' ).append (toHeadingTitle (fileName )).append ('\n' );
145+ if (path .isDirectory ()) {
146+ for (var file : Objects .requireNonNull (path .listFiles ())) {
147+ appendSnippetContent (file , level + 1 );
148+ }
36149 }
37- sb .append ("\n " );
150+ else if (level > 2 && path .getName ().endsWith (".java" )) {
151+ final String fileContent = Files .readString (path .toPath ()),
152+ clientInitEndStr = ".build();\n \n " ,
153+ clientInitStartStr = "VonageClient client" ;
38154
39- for (var file : allDirs ) {
40- appendSnippetContent (sb , file , 2 );
155+ final int clientInitStartIndex = fileContent .indexOf (clientInitStartStr ) - 8 ,
156+ clientInitEndIndex = fileContent .indexOf (clientInitEndStr ) + 11 ,
157+ endIndex = fileContent .lastIndexOf ('}' , fileContent .lastIndexOf ('}' ) - 1 ) - 1 ,
158+ startIndex = Math .min (endIndex , clientInitStartIndex > 0 ?
159+ (isInitialize (path .getParentFile ()) ? clientInitStartIndex : clientInitEndIndex ) :
160+ fileContent .indexOf ('{' , fileContent .indexOf ('{' ) + 1 ) + 2
161+ );
162+
163+ final String nugget = fileContent .substring (startIndex , endIndex )
164+ .stripTrailing ().stripIndent ().replace ("\t " , " " );
165+
166+ sb .append ("\n ```java\n " ).append (nugget ).append ("\n ```\n " );
167+
168+ boolean standalone = clientInitEndIndex < 12 ;
169+ snippetFiles .add (new CodeSnippetFileInfo (path .toPath (),
170+ lineNumberFromIndex (fileContent , startIndex ) + 1 ,
171+ lineNumberFromIndex (fileContent , endIndex ),
172+ standalone ? -1 : lineNumberFromIndex (fileContent , clientInitStartIndex ) + 1 ,
173+ standalone ? -1 : lineNumberFromIndex (fileContent , clientInitEndIndex )
174+ ));
41175 }
176+ }
42177
43- var destPath = repoRoot . resolve ( "SNIPPETS.md" );
44- Files . deleteIfExists ( destPath ) ;
45- Files . writeString ( destPath , sb . toString (), StandardOpenOption . CREATE_NEW ) ;
178+ private static int lineNumberFromIndex ( String content , int index ) {
179+ if ( index < 0 ) return - 1 ;
180+ return content . substring ( 0 , index ). split ( " \n " ). length ;
46181 }
47182
48- static boolean isInitialize (File file ) {
183+ private static boolean isInitialize (File file ) {
49184 return file .getName ().equals ("initialize" );
50185 }
51186
52- static String toHeadingTitle (String title ) {
187+ private static String toHeadingTitle (String title ) {
53188 var acronyms = new String []{
54189 "jwt" , "id" , "uuid" , "url" , "sim" ,
55190 "sms" , "rcs" , "mms" , "psd2" , "dlr" , "cnam" ,
@@ -73,34 +208,4 @@ static String toHeadingTitle(String title) {
73208 }
74209 return result ;
75210 }
76-
77- static void appendSnippetContent (StringBuilder contentBuilder , File path , int level ) throws IOException {
78- var fileName = path .getName ();
79- if (path .isFile ()) {
80- fileName = fileName .substring (0 , fileName .lastIndexOf ('.' ));
81- }
82- if (fileName .trim ().length () < 3 ) return ;
83-
84- contentBuilder .append ("#" .repeat (level )).append (' ' ).append (toHeadingTitle (fileName )).append ('\n' );
85- if (path .isDirectory ()) {
86- for (var file : Objects .requireNonNull (path .listFiles ())) {
87- appendSnippetContent (contentBuilder , file , level + 1 );
88- }
89- }
90- else if (level > 2 && path .getName ().endsWith (".java" )) {
91- final var fileContent = Files .readString (path .toPath ());
92- final var clientInitEndStr = ".build();\n \n " ;
93- final var clientInitStartStr = "VonageClient client" ;
94- final int endIndex = fileContent .lastIndexOf ('}' , fileContent .lastIndexOf ('}' ) - 1 ) - 1 ;
95- final int startIndex = Math .min (endIndex , fileContent .contains (clientInitStartStr ) ?
96- (isInitialize (path .getParentFile ()) ? fileContent .indexOf (clientInitStartStr ) - 8 :
97- fileContent .indexOf (clientInitEndStr ) + clientInitEndStr .length ()) :
98- fileContent .indexOf ('{' , fileContent .indexOf ('{' ) + 1 ) + 2
99- );
100-
101- final var nugget = fileContent .substring (startIndex , endIndex )
102- .stripTrailing ().stripIndent ().replace ("\t " , " " );
103- contentBuilder .append ("\n ```java\n " ).append (nugget ).append ("\n ```\n " );
104- }
105- }
106211}
0 commit comments