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 ;
@@ -91,10 +92,10 @@ private LinkDetection() {
9192
9293 public static List <String > extractLinks (String content , Set <LinkFilter > filter ) {
9394 return new UrlDetector (content , UrlDetectorOptions .BRACKET_MATCH ).detect ()
94- .stream ()
95- .map (url -> toLink (url , filter ))
96- .flatMap (Optional ::stream )
97- .toList ();
95+ .stream ()
96+ .map (url -> toLink (url , filter ))
97+ .flatMap (Optional ::stream )
98+ .toList ();
9899 }
99100
100101 /**
@@ -148,28 +149,28 @@ public static boolean containsLink(String content) {
148149 public static CompletableFuture <Boolean > isLinkBroken (String url ) {
149150 // Try HEAD request first (cheap and fast)
150151 HttpRequest headRequest = HttpRequest .newBuilder (URI .create (url ))
151- .method ("HEAD" , HttpRequest .BodyPublishers .noBody ())
152- .build ();
152+ .method ("HEAD" , HttpRequest .BodyPublishers .noBody ())
153+ .build ();
153154
154155 return HTTP_CLIENT .sendAsync (headRequest , HttpResponse .BodyHandlers .discarding ())
155- .thenApply (response -> {
156- int status = response .statusCode ();
157- // 2xx and 3xx are success, 4xx and 5xx are errors
158- return status >= 400 ;
159- })
160- .exceptionally (_ -> true )
161- .thenCompose (result -> {
162- if (!Boolean .TRUE .equals (result )) {
163- return CompletableFuture .completedFuture (false );
164- }
165- // If HEAD fails, fall back to GET request (some servers don't support HEAD)
166- HttpRequest fallbackGetRequest =
167- HttpRequest .newBuilder (URI .create (url )).GET ().build ();
168- return HTTP_CLIENT
169- .sendAsync (fallbackGetRequest , HttpResponse .BodyHandlers .discarding ())
170- .thenApply (resp -> resp .statusCode () >= 400 )
171- .exceptionally (_ -> true );
172- });
156+ .thenApply (response -> {
157+ int status = response .statusCode ();
158+ // 2xx and 3xx are success, 4xx and 5xx are errors
159+ return status >= 400 ;
160+ })
161+ .exceptionally (_ -> true )
162+ .thenCompose (result -> {
163+ if (!Boolean .TRUE .equals (result )) {
164+ return CompletableFuture .completedFuture (false );
165+ }
166+ // If HEAD fails, fall back to GET request (some servers don't support HEAD)
167+ HttpRequest fallbackGetRequest =
168+ HttpRequest .newBuilder (URI .create (url )).GET ().build ();
169+ return HTTP_CLIENT
170+ .sendAsync (fallbackGetRequest , HttpResponse .BodyHandlers .discarding ())
171+ .thenApply (resp -> resp .statusCode () >= 400 )
172+ .exceptionally (_ -> true );
173+ });
173174 }
174175
175176 /**
@@ -210,31 +211,31 @@ public static CompletableFuture<Boolean> isLinkBroken(String url) {
210211 * text if no broken links were found
211212 */
212213
214+
213215 public static CompletableFuture <String > replaceBrokenLinks (String text , String replacement ) {
214216 List <String > links = extractLinks (text , DEFAULT_FILTERS );
215217
216218 if (links .isEmpty ()) {
217219 return CompletableFuture .completedFuture (text );
218220 }
219221
220- List <CompletableFuture <Optional <String >>> brokenLinkFutures = links .stream ()
221- .distinct ()
222- .map (link -> isLinkBroken (link )
223- .thenApply (isBroken -> Boolean .TRUE .equals (isBroken ) ? Optional .of (link ) : Optional .<String >empty ()))
224- .toList ();
222+ List <CompletableFuture <String >> brokenLinkFutures = links .stream ()
223+ .distinct ()
224+ .map (link -> isLinkBroken (link ).thenApply (isBroken -> isBroken ? link : null ))
225+ .toList ();
225226
226227 return CompletableFuture .allOf (brokenLinkFutures .toArray (new CompletableFuture [0 ]))
227- .thenApply (_ -> brokenLinkFutures .stream ()
228- .map (CompletableFuture ::join )
229- . flatMap ( Optional :: stream )
230- .toList ())
231- .thenApply (brokenLinks -> {
232- String result = text ;
233- for (String brokenLink : brokenLinks ) {
234- result = result .replace (brokenLink , replacement );
235- }
236- return result ;
237- });
228+ .thenApply (_ -> brokenLinkFutures .stream ()
229+ .map (CompletableFuture ::join )
230+ . filter ( Objects :: nonNull )
231+ .toList ())
232+ .thenApply (brokenLinks -> {
233+ String result = text ;
234+ for (String brokenLink : brokenLinks ) {
235+ result = result .replace (brokenLink , replacement );
236+ }
237+ return result ;
238+ });
238239 }
239240
240241 /**
@@ -270,4 +271,4 @@ private static Optional<String> toLink(Url url, Set<LinkFilter> filter) {
270271 }
271272 return Optional .of (link );
272273 }
273- }
274+ }
0 commit comments