1717import java .util .ArrayList ;
1818import java .util .Collections ;
1919import java .util .List ;
20- import java .util .Objects ;
21- import java .util .Optional ;
2220import java .util .Scanner ;
2321import java .util .concurrent .CompletableFuture ;
2422import java .util .regex .Matcher ;
3129/**
3230 * A Javadoc specified by a {@link URI} and containing {@link JavadocElement JavadocElements}.
3331 * Elements are populated by looking at the {@link #INDEX_ALL_PAGE} page of the Javadoc.
32+ *
33+ * @param uri the URI of this Javadoc
34+ * @param elements an unmodifiable view of the elements of this Javadoc
3435 */
35- public class Javadoc {
36+ public record Javadoc ( URI uri , List < JavadocElement > elements ) {
3637
3738 private static final Logger logger = LoggerFactory .getLogger (Javadoc .class );
39+ private static final String INDEX_PAGE = "index.html" ;
40+ private static final String INDEX_ALL_PAGE = "index-all.html" ;
3841 private static final Pattern ENTRY_PATTERN = Pattern .compile ("<dt>(.*?)</dt>" );
3942 private static final Pattern URI_PATTERN = Pattern .compile ("href=\" (.+?)\" " );
4043 private static final Pattern NAME_PATTERN = Pattern .compile ("<a .*?>(?:<span .*?>)?(.*?)(?:</span>)?</a>" );
4144 private static final Pattern CATEGORY_PATTERN = Pattern .compile ("</a> - (.+?) " );
42- private static final String INDEX_PAGE = "index.html" ;
43- private static final String INDEX_ALL_PAGE = "index-all.html" ;
44- private final URI uri ;
45- private final List <JavadocElement > elements ;
45+ private static final int REQUEST_TIMEOUT_SECONDS = 10 ;
4646
47- private Javadoc (URI uri , List <JavadocElement > elements ) {
47+ /**
48+ * Create a Javadoc from a URI and Javadoc elements. Take a look at {@link #create(URI)}
49+ * to create a Javadoc only from a URI.
50+ *
51+ * @param uri the URI pointing to this Javadoc
52+ * @param elements the elements of this Javadoc
53+ */
54+ public Javadoc (URI uri , List <JavadocElement > elements ) {
4855 this .uri = uri ;
4956 this .elements = Collections .unmodifiableList (elements );
5057 }
5158
5259 /**
5360 * Asynchronously attempt to create a Javadoc from the specified URI.
61+ * <p>
62+ * Note that exception handling is left to the caller (the returned CompletableFuture may
63+ * complete exceptionally if the elements of the Javadocs cannot be retrieved for example).
5464 *
55- * @param uri the URI of the Javadoc
56- * @return a CompletableFuture with the created Javadoc, or an empty Optional if the creation failed
65+ * @param uri the URI of the Javadoc
66+ * @return a CompletableFuture (that may complete exceptionally) with the created Javadoc
5767 */
58- public static CompletableFuture <Optional < Javadoc > > create (URI uri ) {
59- return getIndexAllPage (uri ).thenApply (indexAllPage -> indexAllPage . map ( page -> new Javadoc (
68+ public static CompletableFuture <Javadoc > create (URI uri ) {
69+ return getIndexAllPage (uri ).thenApply (indexAllPage -> new Javadoc (
6070 uri ,
6171 parseJavadocIndexPage (
6272 uri .toString ().substring (0 , uri .toString ().lastIndexOf ('/' ) + 1 ),
63- page
73+ indexAllPage
6474 )
65- ))) ;
75+ ));
6676 }
6777
68- @ Override
69- public boolean equals (Object o ) {
70- if (this == o ) return true ;
71- if (o == null || getClass () != o .getClass ()) return false ;
72- Javadoc javadoc = (Javadoc ) o ;
73- return Objects .equals (uri , javadoc .uri );
74- }
75-
76- @ Override
77- public int hashCode () {
78- return Objects .hashCode (uri );
79- }
80-
81- @ Override
82- public String toString () {
83- return "Javadoc{" +
84- "uri=" + uri +
85- ", elements=" + elements +
86- '}' ;
87- }
88-
89- /**
90- * @return the URI of this Javadoc
91- */
92- public URI getUri () {
93- return uri ;
94- }
78+ private static CompletableFuture <String > getIndexAllPage (URI javadocIndexURI ) {
79+ String link = javadocIndexURI .toString ().replace (INDEX_PAGE , INDEX_ALL_PAGE );
80+ URI indexAllURI ;
81+ try {
82+ indexAllURI = new URI (link );
83+ } catch (URISyntaxException e ) {
84+ return CompletableFuture .failedFuture (e );
85+ }
9586
96- /**
97- * @return an unmodifiable view of the elements of this Javadoc
98- */
99- public List <JavadocElement > getElements () {
100- return elements ;
87+ if (Utils .doesUrilinkToWebsite (indexAllURI )) {
88+ return getIndexAllPageContentFromHttp (indexAllURI );
89+ } else {
90+ return CompletableFuture .supplyAsync (() -> {
91+ if (indexAllURI .getScheme ().contains ("jar" )) {
92+ return getIndexAllPageContentFromJar (indexAllURI );
93+ } else {
94+ return getIndexAllPageContentFromNonJar (indexAllURI );
95+ }
96+ });
97+ }
10198 }
10299
103100 private static List <JavadocElement > parseJavadocIndexPage (String javadocURI , String indexHTMLPage ) {
@@ -119,16 +116,18 @@ private static List<JavadocElement> parseJavadocIndexPage(String javadocURI, Str
119116 name = nameMatcher .group (1 ) + "." + name ;
120117 }
121118 String link = javadocURI + uriMatcher .group (1 );
119+ String category = categoryMatcher .group (1 );
120+
121+ name = correctNameIfConstructor (name , category );
122122
123123 try {
124- URI uri = new URI (link );
125124 elements .add (new JavadocElement (
126- uri ,
125+ new URI ( link ) ,
127126 name ,
128- categoryMatcher . group ( 1 )
127+ category
129128 ));
130129 } catch (URISyntaxException e ) {
131- logger .debug (String . format ( "Cannot create URI %s of Javadoc element" , link ) , e );
130+ logger .debug ("Cannot create URI {} of Javadoc element" , link , e );
132131 }
133132 }
134133 }
@@ -137,85 +136,73 @@ private static List<JavadocElement> parseJavadocIndexPage(String javadocURI, Str
137136 return elements ;
138137 }
139138
140- private static CompletableFuture <Optional <String >> getIndexAllPage (URI javadocIndexURI ) {
141- String link = javadocIndexURI .toString ().replace (INDEX_PAGE , INDEX_ALL_PAGE );
142- URI indexAllURI ;
143- try {
144- indexAllURI = new URI (link );
145- } catch (URISyntaxException e ) {
146- logger .debug (String .format ("Cannot create URI %s of index page" , link ), e );
147- return CompletableFuture .completedFuture (Optional .empty ());
148- }
139+ private static CompletableFuture <String > getIndexAllPageContentFromHttp (URI uri ) {
140+ HttpClient httpClient = HttpClient .newBuilder ()
141+ .followRedirects (HttpClient .Redirect .ALWAYS )
142+ .build ();
149143
150- if (indexAllURI .getScheme ().contains ("http" )) {
151- return getIndexAllPageFromHttp (indexAllURI );
152- } else {
153- return CompletableFuture .supplyAsync (() -> {
154- if (indexAllURI .getScheme ().contains ("jar" )) {
155- return getIndexAllPageFromJar (indexAllURI );
156- } else {
157- return getIndexAllPageFromDirectory (indexAllURI );
158- }
159- });
160- }
161- }
144+ logger .debug ("Sending GET request to {} to read the index-all page content..." , uri );
162145
163- private static CompletableFuture <Optional <String >> getIndexAllPageFromHttp (URI uri ) {
164- return HttpClient .newHttpClient ().sendAsync (
146+ return httpClient .sendAsync (
165147 HttpRequest .newBuilder ()
166148 .uri (uri )
167- .timeout (Duration .of (10 , ChronoUnit .SECONDS ))
149+ .timeout (Duration .of (REQUEST_TIMEOUT_SECONDS , ChronoUnit .SECONDS ))
168150 .GET ()
169151 .build (),
170152 HttpResponse .BodyHandlers .ofString ()
171- ).handle ((response , error ) -> {
172- if (response == null || error != null ) {
173- if (error != null ) {
174- logger .debug ("Error when retrieving Javadoc index page" , error );
175- }
176- return Optional .empty ();
177- } else {
178- return Optional .ofNullable (response .body ());
179- }
180- });
153+ ).thenApply (response -> {
154+ logger .debug ("Got response {} from {}" , response , uri );
155+ return response .body ();
156+ }).whenComplete ((b , e ) -> httpClient .close ());
181157 }
182158
183- private static Optional < String > getIndexAllPageFromJar (URI uri ) {
159+ private static String getIndexAllPageContentFromJar (URI uri ) {
184160 String jarURI = uri .toString ().substring (
185161 uri .toString ().indexOf ('/' ),
186162 uri .toString ().lastIndexOf ('!' )
187163 );
164+ logger .debug ("Opening {} jar file to read the index-all page content..." , jarURI );
188165
189166 try (ZipFile zipFile = new ZipFile (jarURI )) {
190167 ZipEntry entry = zipFile .getEntry (INDEX_ALL_PAGE );
191168
192169 if (entry == null ) {
193- logger .debug (String .format ("%s not found in %s" , INDEX_ALL_PAGE , jarURI ));
194- return Optional .empty ();
170+ throw new IllegalArgumentException (String .format ("The provided jar file %s doesn't contain any %s entry" , jarURI , INDEX_ALL_PAGE ));
195171 } else {
196172 try (
197- InputStream inputStream = zipFile .getInputStream (zipFile . getEntry ( INDEX_ALL_PAGE ) );
173+ InputStream inputStream = zipFile .getInputStream (entry );
198174 Scanner scanner = new Scanner (inputStream )
199175 ) {
200176 StringBuilder lines = new StringBuilder ();
201177 while (scanner .hasNextLine ()) {
202178 lines .append (scanner .nextLine ());
203179 }
204- return Optional . of ( lines .toString () );
180+ return lines .toString ();
205181 }
206182 }
207183 } catch (IOException e ) {
208- logger .debug (String .format ("Error while reading %s" , jarURI ), e );
209- return Optional .empty ();
184+ throw new RuntimeException (e );
210185 }
211186 }
212187
213- private static Optional <String > getIndexAllPageFromDirectory (URI uri ) {
188+ private static String getIndexAllPageContentFromNonJar (URI uri ) {
189+ logger .debug ("Reading {} file to get the index-all page content..." , uri );
190+
214191 try (Stream <String > lines = Files .lines (Paths .get (uri ))) {
215- return Optional .of (lines .collect (Collectors .joining ("\n " )));
216- } catch (Exception e ) {
217- logger .debug (String .format ("Error while reading %s" , uri ), e );
218- return Optional .empty ();
192+ return lines .collect (Collectors .joining ("\n " ));
193+ } catch (IOException e ) {
194+ throw new RuntimeException (e );
195+ }
196+ }
197+
198+ private static String correctNameIfConstructor (String name , String category ) {
199+ // Constructor are usually written in the following way: "Class.Class(Parameter)"
200+ // This function transforms them into "Class(Parameter)"
201+ if (category .equals ("Constructor" )) {
202+ int pointIndex = name .indexOf ("." );
203+ return name .substring (pointIndex + 1 );
204+ } else {
205+ return name ;
219206 }
220207 }
221208}
0 commit comments