99import java .net .http .HttpRequest ;
1010import java .net .http .HttpResponse ;
1111import java .util .List ;
12+ import java .util .Objects ;
1213import java .util .Optional ;
1314import java .util .Set ;
1415import java .util .concurrent .CompletableFuture ;
@@ -210,35 +211,35 @@ public static CompletableFuture<Boolean> isLinkBroken(String url) {
210211 * text if no broken links were found
211212 */
212213
213-
214214 public static CompletableFuture <String > replaceBrokenLinks (String text , String replacement ) {
215215 List <String > links = extractLinks (text , DEFAULT_FILTERS );
216216
217217 if (links .isEmpty ()) {
218218 return CompletableFuture .completedFuture (text );
219219 }
220220
221- List <String > distinctLinks = links .stream ().distinct ().toList ();
222- List <CompletableFuture <Boolean >> brokenCheckFutures =
223- distinctLinks .stream ().map (LinkDetection ::isLinkBroken ).toList ();
221+ // Can't filter yet - we won't know which links are broken until the futures complete
222+ List <CompletableFuture <String >> brokenLinkFutures = links .stream ()
223+ .distinct ()
224+ .map (link -> isLinkBroken (link )
225+ .thenApply (isBroken -> Boolean .TRUE .equals (isBroken ) ? link : null ))
226+ .toList ();
224227
225- return CompletableFuture .allOf (brokenCheckFutures .toArray (new CompletableFuture [0 ]))
226- .thenApply (_ -> {
227- List <String > brokenLinks = new java .util .ArrayList <>();
228- for (int i = 0 ; i < distinctLinks .size (); i ++) {
229- if (Boolean .TRUE .equals (brokenCheckFutures .get (i ).join ())) {
230- brokenLinks .add (distinctLinks .get (i ));
231- }
232- }
233- return brokenLinks ;
234- })
235- .thenApply (brokenLinks -> {
236- String result = text ;
237- for (String brokenLink : brokenLinks ) {
238- result = result .replace (brokenLink , replacement );
239- }
240- return result ;
241- });
228+ return CompletableFuture .allOf (brokenLinkFutures .toArray (CompletableFuture []::new ))
229+ .thenApply (_ -> brokenLinkFutures .stream ()
230+ .map (CompletableFuture ::join )
231+ .filter (Objects ::nonNull )
232+ .toList ())
233+ .thenApply (brokenLinks -> replaceLinks (brokenLinks , text , replacement ));
234+ }
235+
236+ private static String replaceLinks (List <String > linksToReplace , String text ,
237+ String replacement ) {
238+ String result = text ;
239+ for (String link : linksToReplace ) {
240+ result = result .replace (link , replacement );
241+ }
242+ return result ;
242243 }
243244
244245 /**
@@ -260,7 +261,8 @@ private static Optional<String> toLink(Url url, Set<LinkFilter> filter) {
260261 // URL escapes, such as "<http://example.com>" should be skipped
261262 return Optional .empty ();
262263 }
263- // Not interested in other schemes, also to filter out matches without scheme.
264+ // Not interested in other schemes, also to filter out matches without scheme (Skip non-HTTP
265+ // schemes)
264266 // It detects a lot of such false-positives in Java snippets
265267 if (filter .contains (LinkFilter .NON_HTTP_SCHEME ) && !raw .startsWith ("http" )) {
266268 return Optional .empty ();
0 commit comments