Skip to content

Commit 2523c39

Browse files
authored
Merge pull request #9 from qupath/improved-search
Improved search
2 parents 820e859 + bc78606 commit 2523c39

File tree

13 files changed

+286
-178
lines changed

13 files changed

+286
-178
lines changed

.github/workflows/gradle.yaml

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,16 @@ jobs:
2424

2525
steps:
2626
- uses: actions/checkout@v4
27-
- name: Set up JDK 17
27+
- name: Set up JDK 21
2828
uses: actions/setup-java@v3
2929
with:
30-
java-version: '17'
30+
java-version: '21'
3131
distribution: 'temurin'
3232
- name: Validate Gradle wrapper
33-
uses: gradle/wrapper-validation-action@v1
34-
- name: Build with Gradle
35-
uses: gradle/gradle-build-action@v2
36-
with:
37-
arguments: build
38-
- uses: actions/upload-artifact@v3
33+
uses: gradle/actions/wrapper-validation@v3
34+
- name: Build with gradle
35+
run: ./gradlew build
36+
- uses: actions/upload-artifact@v4
3937
with:
4038
name: javadoc-viewer-jar
4139
path: build/libs

.github/workflows/publish-release.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,21 @@ jobs:
1111

1212
steps:
1313
- uses: actions/checkout@v4
14-
- name: Set up JDK 17
14+
- name: Set up JDK 21
1515
uses: actions/setup-java@v3
1616
with:
17-
java-version: '17'
17+
java-version: '21'
1818
distribution: 'temurin'
1919
- name: Validate Gradle wrapper
20-
uses: gradle/wrapper-validation-action@v1
20+
uses: gradle/actions/wrapper-validation@v3
2121
- name: Publish snapshot
2222
uses: gradle/gradle-build-action@v2
2323
with:
2424
arguments: publish -P release=true
2525
env:
2626
MAVEN_USER: ${{ secrets.MAVEN_USER }}
2727
MAVEN_PASS: ${{ secrets.MAVEN_PASS }}
28-
- uses: actions/upload-artifact@v3
28+
- uses: actions/upload-artifact@v4
2929
with:
3030
name: javadoc-viewer-release-jar
3131
path: build/libs

.github/workflows/publish-snapshot.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@ jobs:
1010

1111
steps:
1212
- uses: actions/checkout@v4
13-
- name: Set up JDK 17
13+
- name: Set up JDK 21
1414
uses: actions/setup-java@v3
1515
with:
16-
java-version: '17'
16+
java-version: '21'
1717
distribution: 'temurin'
1818
- name: Validate Gradle wrapper
19-
uses: gradle/wrapper-validation-action@v1
19+
uses: gradle/actions/wrapper-validation@v3
2020
- name: Publish snapshot
2121
uses: gradle/gradle-build-action@v2
2222
with:
2323
arguments: publish
2424
env:
2525
MAVEN_USER: ${{ secrets.MAVEN_USER }}
2626
MAVEN_PASS: ${{ secrets.MAVEN_PASS }}
27-
- uses: actions/upload-artifact@v3
27+
- uses: actions/upload-artifact@v4
2828
with:
2929
name: javadoc-viewer-snapshot-jar
3030
path: build/libs

gradle/libs.versions.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[versions]
2-
java = "17"
2+
java = "21"
33
slf4j = "2.0.7"
4-
javafx = "20"
4+
javafx = "21.0.6"
55
javafxPlugin = "0.1.0"
66

77
[libraries]

javadocviewer/src/main/java/qupath/ui/javadocviewer/core/Javadoc.java

Lines changed: 83 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
import java.util.ArrayList;
1818
import java.util.Collections;
1919
import java.util.List;
20-
import java.util.Objects;
21-
import java.util.Optional;
2220
import java.util.Scanner;
2321
import java.util.concurrent.CompletableFuture;
2422
import java.util.regex.Matcher;
@@ -31,73 +29,72 @@
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
}

javadocviewer/src/main/java/qupath/ui/javadocviewer/core/JavadocElement.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
/**
66
* An element (function, class, enum...) of a Javadoc.
77
*
8-
* @param uri the URI of the Javadoc owning this element
9-
* @param name the name of the element (e.g. the function name)
10-
* @param category the category of the element (e.g. "function")
8+
* @param uri the URI of the Javadoc owning this element
9+
* @param name the name of the element (e.g. the function name)
10+
* @param category the category of the element (e.g. "function" or "class")
1111
*/
1212
public record JavadocElement(URI uri, String name, String category) {}

0 commit comments

Comments
 (0)