Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version = "0.1.2"
version = "0.1.3"
152 changes: 152 additions & 0 deletions javadocviewer/src/main/java/qupath/ui/javadocviewer/UriUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package qupath.ui.javadocviewer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
* A collection of utility functions to work with {@link URI}.
*/
public class UriUtils {

private static final List<String> WEBSITE_SCHEMES = List.of("http", "https");
private static final int REQUEST_TIMEOUT_SECONDS = 10;
private static final Logger logger = LoggerFactory.getLogger(UriUtils.class);

private UriUtils() {
throw new AssertionError("This class is not instantiable.");
}

/**
* Indicate whether the provided URI links to a website.
*
* @param uri the URI to check
* @return whether the provided URI links to a website
*/
public static boolean doesUriLinkToWebsite(URI uri) {
return uri.getScheme() != null && WEBSITE_SCHEMES.contains(uri.getScheme());
}

/**
* Indicate whether the provided URI links to a jar file or a file contained in a jar file.
*
* @param uri the URI to check
* @return whether the provided URI links to a jar file or a file contained in a jar file
*/
public static boolean doesUriLinkToJar(URI uri) {
return uri.getScheme() != null && uri.getScheme().contains("jar");
}

/**
* Attempt to read the resource pointed by the provided URI.
* <p>
* This function supports reading http or https links, files contained in local jars, and local files.
* <p>
* The returned CompletableFuture may complete exceptionally.
*
* @param uri the URI pointing to the resource to read
* @return the resource pointed by the provided URI, or a failed CompletableFuture if the reading did not succeed
*/
public static CompletableFuture<String> getContentOfUri(URI uri) {
if (doesUriLinkToWebsite(uri)) {
return getContentOfHttpUri(uri);
} else if (doesUriLinkToJar(uri)) {
return CompletableFuture.supplyAsync(() -> getContentOfJarUri(uri));
} else if (uri.getScheme().contains("file")) {
return CompletableFuture.supplyAsync(() -> getContentOfFileUri(uri));
} else {
return CompletableFuture.failedFuture(new IllegalArgumentException(String.format(
"The provided URI %s does not point to a website, jar or file",
uri
)));
}
}

private static CompletableFuture<String> getContentOfHttpUri(URI uri) {
HttpClient httpClient = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS)
.build();

logger.debug("Sending GET request to {}...", uri);

return httpClient.sendAsync(
HttpRequest.newBuilder()
.uri(uri)
.timeout(Duration.of(REQUEST_TIMEOUT_SECONDS, ChronoUnit.SECONDS))
.GET()
.build(),
HttpResponse.BodyHandlers.ofString()
).thenApply(response -> {
logger.debug("Got response {} from {}", response, uri);
return response.body();
}).whenComplete((b, e) -> httpClient.close());
}

private static String getContentOfJarUri(URI uri) {
// The provided URI is expected to be like: jar:file:/path/to/some-javadoc.jar!/index.html#someParameters
String jarUri = uri.toString().substring(
uri.toString().indexOf('/'),
uri.toString().lastIndexOf('!')
);
logger.debug("Opening {} jar file to read the content of {}...", jarUri, uri);

try (ZipFile zipFile = new ZipFile(jarUri)) {
String entryName = uri.toString().substring(
uri.toString().lastIndexOf("!/") + 2,
uri.toString().lastIndexOf('#') == -1 ? uri.toString().length() : uri.toString().lastIndexOf('#')
);
ZipEntry entry = zipFile.getEntry(entryName);
if (entry == null) {
throw new IllegalArgumentException(String.format("%s not found in %s", entryName, jarUri));
}

try (
InputStream inputStream = zipFile.getInputStream(entry);
Scanner scanner = new Scanner(inputStream)
) {
StringBuilder lines = new StringBuilder();
while (scanner.hasNextLine()) {
lines.append(scanner.nextLine());
}
return lines.toString();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static String getContentOfFileUri(URI uri) {
logger.debug("Reading {} file...", uri);

URI uriWithoutFragment;
try {
uriWithoutFragment = new URI(uri.getScheme(), uri.getSchemeSpecificPart(), null);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}

try (Stream<String> lines = Files.lines(Paths.get(uriWithoutFragment))) {
return lines.collect(Collectors.joining("\n"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,16 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qupath.ui.javadocviewer.UriUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
* A Javadoc specified by a {@link URI} and containing {@link JavadocElement JavadocElements}.
Expand All @@ -42,7 +29,6 @@ public record Javadoc(URI uri, List<JavadocElement> elements) {
private static final Pattern URI_PATTERN = Pattern.compile("href=\"(.+?)\"");
private static final Pattern NAME_PATTERN = Pattern.compile("<a .*?>(?:<span .*?>)?(.*?)(?:</span>)?</a>");
private static final Pattern CATEGORY_PATTERN = Pattern.compile("</a> - (.+?) ");
private static final int REQUEST_TIMEOUT_SECONDS = 10;

/**
* Create a Javadoc from a URI and Javadoc elements. Take a look at {@link #create(URI)}
Expand Down Expand Up @@ -77,24 +63,14 @@ public static CompletableFuture<Javadoc> create(URI uri) {

private static CompletableFuture<String> getIndexAllPage(URI javadocIndexURI) {
String link = javadocIndexURI.toString().replace(INDEX_PAGE, INDEX_ALL_PAGE);
URI indexAllURI;
URI indexAllUri;
try {
indexAllURI = new URI(link);
indexAllUri = new URI(link);
} catch (URISyntaxException e) {
return CompletableFuture.failedFuture(e);
}

if (Utils.doesUrilinkToWebsite(indexAllURI)) {
return getIndexAllPageContentFromHttp(indexAllURI);
} else {
return CompletableFuture.supplyAsync(() -> {
if (indexAllURI.getScheme().contains("jar")) {
return getIndexAllPageContentFromJar(indexAllURI);
} else {
return getIndexAllPageContentFromNonJar(indexAllURI);
}
});
}
return UriUtils.getContentOfUri(indexAllUri);
}

private static List<JavadocElement> parseJavadocIndexPage(String javadocURI, String indexHTMLPage) {
Expand Down Expand Up @@ -136,65 +112,6 @@ private static List<JavadocElement> parseJavadocIndexPage(String javadocURI, Str
return elements;
}

private static CompletableFuture<String> getIndexAllPageContentFromHttp(URI uri) {
HttpClient httpClient = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS)
.build();

logger.debug("Sending GET request to {} to read the index-all page content...", uri);

return httpClient.sendAsync(
HttpRequest.newBuilder()
.uri(uri)
.timeout(Duration.of(REQUEST_TIMEOUT_SECONDS, ChronoUnit.SECONDS))
.GET()
.build(),
HttpResponse.BodyHandlers.ofString()
).thenApply(response -> {
logger.debug("Got response {} from {}", response, uri);
return response.body();
}).whenComplete((b, e) -> httpClient.close());
}

private static String getIndexAllPageContentFromJar(URI uri) {
String jarURI = uri.toString().substring(
uri.toString().indexOf('/'),
uri.toString().lastIndexOf('!')
);
logger.debug("Opening {} jar file to read the index-all page content...", jarURI);

try (ZipFile zipFile = new ZipFile(jarURI)) {
ZipEntry entry = zipFile.getEntry(INDEX_ALL_PAGE);

if (entry == null) {
throw new IllegalArgumentException(String.format("The provided jar file %s doesn't contain any %s entry", jarURI, INDEX_ALL_PAGE));
} else {
try (
InputStream inputStream = zipFile.getInputStream(entry);
Scanner scanner = new Scanner(inputStream)
) {
StringBuilder lines = new StringBuilder();
while (scanner.hasNextLine()) {
lines.append(scanner.nextLine());
}
return lines.toString();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static String getIndexAllPageContentFromNonJar(URI uri) {
logger.debug("Reading {} file to get the index-all page content...", uri);

try (Stream<String> lines = Files.lines(Paths.get(uri))) {
return lines.collect(Collectors.joining("\n"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static String correctNameIfConstructor(String name, String category) {
// Constructor are usually written in the following way: "Class.Class(Parameter)"
// This function transforms them into "Class(Parameter)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qupath.ui.javadocviewer.UriUtils;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -51,7 +52,7 @@ public static CompletableFuture<List<Javadoc>> findJavadocs(URI... urisToSearch)
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
logger.debug("Error when creating javadoc of {}. Skipping it", uri, e);
logger.warn("Error when creating javadoc of {}. Skipping it", uri, e);

return null;
}
Expand All @@ -63,7 +64,7 @@ public static CompletableFuture<List<Javadoc>> findJavadocs(URI... urisToSearch)
}

private static List<URI> findJavadocUrisFromUri(URI uri) {
if (Utils.doesUrilinkToWebsite(uri)) {
if (UriUtils.doesUriLinkToWebsite(uri)) {
logger.debug("URI {} retrieved", uri);
return List.of(uri);
} else {
Expand Down

This file was deleted.

Loading
Loading