")
+ .addClass(category === "modules"
+ ? "one-column-search-results"
+ : "two-column-search-results");
+ var col1, col2;
+ if (category === "modules") {
+ col1 = "Module";
+ } else if (category === "packages") {
+ col1 = "Module";
+ col2 = "Package";
+ } else if (category === "types") {
+ col1 = "Package";
+ col2 = "Class"
+ } else if (category === "members") {
+ col1 = "Class";
+ col2 = "Member";
+ } else if (category === "searchTags") {
+ col1 = "Location";
+ col2 = "Name";
+ }
+ $("").appendTo(table);
+ if (category !== "modules") {
+ $("").appendTo(table);
+ }
+ $.each(items, function(index, item) {
+ var rowColor = index % 2 ? "odd-row-color" : "even-row-color";
+ renderItem(item, table, rowColor);
+ });
+ return table;
+ }
+ function renderItem(item, table, rowColor) {
+ var label = getHighlightedText(item.input, item.boundaries, item.prefix.length, item.input.length);
+ var link = $("
")
+ .attr("href", getURL(item.indexItem, item.category))
+ .attr("tabindex", "0")
+ .addClass("search-result-link")
+ .html(label);
+ var container = getHighlightedText(item.input, item.boundaries, 0, item.prefix.length - 1);
+ if (item.category === "searchTags") {
+ container = item.indexItem.h || "";
+ }
+ if (item.category !== "modules") {
+ $("
").html(container).addClass("col-plain").addClass(rowColor).appendTo(table);
+ }
+ $("
").html(link).addClass("col-last").addClass(rowColor).appendTo(table);
+ }
+ var timeout;
+ function schedulePageSearch() {
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+ timeout = setTimeout(function () {
+ doPageSearch()
+ }, 100);
+ }
+ function doPageSearch() {
+ setSearchUrl();
+ var term = searchTerm = input.val().trim();
+ if (term === "") {
+ notify.html(messages.enterTerm);
+ activeTab = "";
+ fixedTab = false;
+ resultContainer.empty();
+ resultSection.hide();
+ } else {
+ notify.html(messages.searching);
+ doSearch({ term: term, maxResults: 1200 }, renderResults);
+ }
+ }
+ function setSearchUrl() {
+ var query = input.val().trim();
+ var url = document.location.pathname;
+ if (query) {
+ url += "?q=" + encodeURI(query);
+ if (activeTab && fixedTab) {
+ url += "&c=" + activeTab;
+ }
+ }
+ history.replaceState({query: query}, "", url);
+ }
+ input.on("input", function(e) {
+ feelingLucky = false;
+ schedulePageSearch();
+ });
+ $(document).keydown(function(e) {
+ if ((e.ctrlKey || e.metaKey) && (e.key === "ArrowLeft" || e.key === "ArrowRight")) {
+ if (activeTab && visibleTabs.length > 1) {
+ var idx = visibleTabs.indexOf(activeTab);
+ idx += e.key === "ArrowLeft" ? visibleTabs.length - 1 : 1;
+ selectTab(visibleTabs[idx % visibleTabs.length]);
+ return false;
+ }
+ }
+ });
+ reset.click(function() {
+ notify.html(messages.enterTerm);
+ resultSection.hide();
+ activeTab = "";
+ fixedTab = false;
+ resultContainer.empty();
+ input.val('').focus();
+ setSearchUrl();
+ });
+ input.prop("disabled", false);
+ reset.prop("disabled", false);
+
+ var urlParams = new URLSearchParams(window.location.search);
+ if (urlParams.has("q")) {
+ input.val(urlParams.get("q"))
+ }
+ if (urlParams.has("c")) {
+ activeTab = urlParams.get("c");
+ fixedTab = true;
+ }
+ if (urlParams.get("r")) {
+ feelingLucky = true;
+ }
+ if (input.val()) {
+ doPageSearch();
+ } else {
+ notify.html(messages.enterTerm);
+ }
+ input.select().focus();
+});
diff --git a/docs/script-files/search.js b/docs/script-files/search.js
new file mode 100644
index 0000000..da9e541
--- /dev/null
+++ b/docs/script-files/search.js
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+"use strict";
+const messages = {
+ enterTerm: "Geben Sie einen Suchbegriff ein",
+ noResult: "Keine Ergebnisse gefunden",
+ oneResult: "Ein Ergebnis gefunden",
+ manyResults: "{0} Ergebnisse gefunden",
+ loading: "Suchindex wird geladen...",
+ searching: "Suche wird ausgeführt...",
+ redirecting: "Zum ersten Ergebnis wird umgeleitet...",
+ linkIcon: "Linksymbol",
+ linkToSection: "Link zu diesem Abschnitt"
+}
+const categories = {
+ modules: "Module",
+ packages: "Packages",
+ types: "Klassen und Schnittstellen",
+ members: "Mitglieder",
+ searchTags: "Tags suchen"
+};
+const highlight = "
$&";
+const NO_MATCH = {};
+const MAX_RESULTS = 300;
+const UNICODE_LETTER = 0;
+const UNICODE_DIGIT = 1;
+const UNICODE_OTHER = 2;
+function checkUnnamed(name, separator) {
+ return name === "
" || !name ? "" : name + separator;
+}
+function escapeHtml(str) {
+ return str.replace(//g, ">");
+}
+function getHighlightedText(str, boundaries, from, to) {
+ var start = from;
+ var text = "";
+ for (var i = 0; i < boundaries.length; i += 2) {
+ var b0 = boundaries[i];
+ var b1 = boundaries[i + 1];
+ if (b0 >= to || b1 <= from) {
+ continue;
+ }
+ text += escapeHtml(str.slice(start, Math.max(start, b0)));
+ text += "";
+ text += escapeHtml(str.slice(Math.max(start, b0), Math.min(to, b1)));
+ text += "";
+ start = Math.min(to, b1);
+ }
+ text += escapeHtml(str.slice(start, to));
+ return text;
+}
+function getURLPrefix(item, category) {
+ var urlPrefix = "";
+ var slash = "/";
+ if (category === "modules") {
+ return item.l + slash;
+ } else if (category === "packages" && item.m) {
+ return item.m + slash;
+ } else if (category === "types" || category === "members") {
+ if (item.m) {
+ urlPrefix = item.m + slash;
+ } else {
+ $.each(packageSearchIndex, function(index, it) {
+ if (it.m && item.p === it.l) {
+ urlPrefix = it.m + slash;
+ }
+ });
+ }
+ }
+ return urlPrefix;
+}
+function getURL(item, category) {
+ if (item.url) {
+ return item.url;
+ }
+ var url = getURLPrefix(item, category);
+ if (category === "modules") {
+ url += "module-summary.html";
+ } else if (category === "packages") {
+ if (item.u) {
+ url = item.u;
+ } else {
+ url += item.l.replace(/\./g, '/') + "/package-summary.html";
+ }
+ } else if (category === "types") {
+ if (item.u) {
+ url = item.u;
+ } else {
+ url += checkUnnamed(item.p, "/").replace(/\./g, '/') + item.l + ".html";
+ }
+ } else if (category === "members") {
+ url += checkUnnamed(item.p, "/").replace(/\./g, '/') + item.c + ".html" + "#";
+ if (item.u) {
+ url += item.u;
+ } else {
+ url += item.l;
+ }
+ } else if (category === "searchTags") {
+ url += item.u;
+ }
+ item.url = url;
+ return url;
+}
+function createMatcher(term, camelCase) {
+ if (camelCase && !isUpperCase(term)) {
+ return null; // no need for camel-case matcher for lower case query
+ }
+ var pattern = "";
+ var upperCase = [];
+ term.trim().split(/\s+/).forEach(function(w, index, array) {
+ var tokens = w.split(/(?=[\p{Lu},.()<>?[\/])/u);
+ for (var i = 0; i < tokens.length; i++) {
+ var s = tokens[i];
+ // ',' and '?' are the only delimiters commonly followed by space in java signatures
+ pattern += "(" + escapeUnicodeRegex(s).replace(/[,?]/g, "$&\\s*?") + ")";
+ upperCase.push(false);
+ var isWordToken = /[\p{L}\p{Nd}_]$/u.test(s);
+ if (isWordToken) {
+ if (i === tokens.length - 1 && index < array.length - 1) {
+ // space in query string matches all delimiters
+ pattern += "(.*?)";
+ upperCase.push(isUpperCase(s[0]));
+ } else {
+ if (!camelCase && isUpperCase(s) && s.length === 1) {
+ pattern += "()";
+ } else {
+ pattern += "([\\p{L}\\p{Nd}\\p{Sc}<>?[\\]]*?)";
+ }
+ upperCase.push(isUpperCase(s[0]));
+ }
+ } else {
+ pattern += "()";
+ upperCase.push(false);
+ }
+ }
+ });
+ var re = new RegExp(pattern, "gui");
+ re.upperCase = upperCase;
+ return re;
+}
+// Unicode regular expressions do not allow certain characters to be escaped
+function escapeUnicodeRegex(pattern) {
+ return pattern.replace(/[\[\]{}()*+?.\\^$|\s]/g, '\\$&');
+}
+function findMatch(matcher, input, startOfName, endOfName) {
+ var from = startOfName;
+ matcher.lastIndex = from;
+ var match = matcher.exec(input);
+ // Expand search area until we get a valid result or reach the beginning of the string
+ while (!match || match.index + match[0].length < startOfName || endOfName < match.index) {
+ if (from === 0) {
+ return NO_MATCH;
+ }
+ from = input.lastIndexOf(".", from - 2) + 1;
+ matcher.lastIndex = from;
+ match = matcher.exec(input);
+ }
+ var boundaries = [];
+ var matchEnd = match.index + match[0].length;
+ var score = 5;
+ var start = match.index;
+ var prevEnd = -1;
+ for (var i = 1; i < match.length; i += 2) {
+ var charType = getCharType(input[start]);
+ var isMatcherUpper = matcher.upperCase[i];
+ // capturing groups come in pairs, match and non-match
+ boundaries.push(start, start + match[i].length);
+ // make sure groups are anchored on a left word boundary
+ var prevChar = input[start - 1] || "";
+ var nextChar = input[start + 1] || "";
+ if (start !== 0) {
+ if (charType === UNICODE_DIGIT && getCharType(prevChar) === UNICODE_DIGIT) {
+ return NO_MATCH;
+ } else if (charType === UNICODE_LETTER && getCharType(prevChar) === UNICODE_LETTER) {
+ var isUpper = isUpperCase(input[start]);
+ if (isUpper && (isLowerCase(prevChar) || isLowerCase(nextChar))) {
+ score -= 0.1;
+ } else if (isMatcherUpper && start === prevEnd) {
+ score -= isUpper ? 0.1 : 1.0;
+ } else {
+ return NO_MATCH;
+ }
+ }
+ }
+ prevEnd = start + match[i].length;
+ start += match[i].length + match[i + 1].length;
+
+ // lower score for parts of the name that are missing
+ if (match[i + 1] && prevEnd < endOfName) {
+ score -= rateNoise(match[i + 1]);
+ }
+ }
+ // lower score if a type name contains unmatched camel-case parts
+ if (input[matchEnd - 1] !== "." && endOfName > matchEnd)
+ score -= rateNoise(input.slice(matchEnd, endOfName));
+ score -= rateNoise(input.slice(0, Math.max(startOfName, match.index)));
+
+ if (score <= 0) {
+ return NO_MATCH;
+ }
+ return {
+ input: input,
+ score: score,
+ boundaries: boundaries
+ };
+}
+function isLetter(s) {
+ return /\p{L}/u.test(s);
+}
+function isUpperCase(s) {
+ return /\p{Lu}/u.test(s);
+}
+function isLowerCase(s) {
+ return /\p{Ll}/u.test(s);
+}
+function isDigit(s) {
+ return /\p{Nd}/u.test(s);
+}
+function getCharType(s) {
+ if (isLetter(s)) {
+ return UNICODE_LETTER;
+ } else if (isDigit(s)) {
+ return UNICODE_DIGIT;
+ } else {
+ return UNICODE_OTHER;
+ }
+}
+function rateNoise(str) {
+ return (str.match(/([.(])/g) || []).length / 5
+ + (str.match(/(\p{Lu}+)/gu) || []).length / 10
+ + str.length / 20;
+}
+function doSearch(request, response) {
+ var term = request.term.trim();
+ var maxResults = request.maxResults || MAX_RESULTS;
+ if (term.length === 0) {
+ return this.close();
+ }
+ var matcher = {
+ plainMatcher: createMatcher(term, false),
+ camelCaseMatcher: createMatcher(term, true)
+ }
+ var indexLoaded = indexFilesLoaded();
+
+ function getPrefix(item, category) {
+ switch (category) {
+ case "packages":
+ return checkUnnamed(item.m, "/");
+ case "types":
+ return checkUnnamed(item.p, ".");
+ case "members":
+ return checkUnnamed(item.p, ".") + item.c + ".";
+ default:
+ return "";
+ }
+ }
+ function useQualifiedName(category) {
+ switch (category) {
+ case "packages":
+ return /[\s/]/.test(term);
+ case "types":
+ case "members":
+ return /[\s.]/.test(term);
+ default:
+ return false;
+ }
+ }
+ function searchIndex(indexArray, category) {
+ var matches = [];
+ if (!indexArray) {
+ if (!indexLoaded) {
+ matches.push({ l: messages.loading, category: category });
+ }
+ return matches;
+ }
+ $.each(indexArray, function (i, item) {
+ var prefix = getPrefix(item, category);
+ var simpleName = item.l;
+ var qualifiedName = prefix + simpleName;
+ var useQualified = useQualifiedName(category);
+ var input = useQualified ? qualifiedName : simpleName;
+ var startOfName = useQualified ? prefix.length : 0;
+ var endOfName = category === "members" && input.indexOf("(", startOfName) > -1
+ ? input.indexOf("(", startOfName) : input.length;
+ var m = findMatch(matcher.plainMatcher, input, startOfName, endOfName);
+ if (m === NO_MATCH && matcher.camelCaseMatcher) {
+ m = findMatch(matcher.camelCaseMatcher, input, startOfName, endOfName);
+ }
+ if (m !== NO_MATCH) {
+ m.indexItem = item;
+ m.prefix = prefix;
+ m.category = category;
+ if (!useQualified) {
+ m.input = qualifiedName;
+ m.boundaries = m.boundaries.map(function(b) {
+ return b + prefix.length;
+ });
+ }
+ matches.push(m);
+ }
+ return true;
+ });
+ return matches.sort(function(e1, e2) {
+ return e2.score - e1.score;
+ }).slice(0, maxResults);
+ }
+
+ var result = searchIndex(moduleSearchIndex, "modules")
+ .concat(searchIndex(packageSearchIndex, "packages"))
+ .concat(searchIndex(typeSearchIndex, "types"))
+ .concat(searchIndex(memberSearchIndex, "members"))
+ .concat(searchIndex(tagSearchIndex, "searchTags"));
+
+ if (!indexLoaded) {
+ updateSearchResults = function() {
+ doSearch(request, response);
+ }
+ } else {
+ updateSearchResults = function() {};
+ }
+ response(result);
+}
+// JQuery search menu implementation
+$.widget("custom.catcomplete", $.ui.autocomplete, {
+ _create: function() {
+ this._super();
+ this.widget().menu("option", "items", "> .result-item");
+ // workaround for search result scrolling
+ this.menu._scrollIntoView = function _scrollIntoView( item ) {
+ var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
+ if ( this._hasScroll() ) {
+ borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0;
+ paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0;
+ offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
+ scroll = this.activeMenu.scrollTop();
+ elementHeight = this.activeMenu.height() - 26;
+ itemHeight = item.outerHeight();
+
+ if ( offset < 0 ) {
+ this.activeMenu.scrollTop( scroll + offset );
+ } else if ( offset + itemHeight > elementHeight ) {
+ this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
+ }
+ }
+ };
+ },
+ _renderMenu: function(ul, items) {
+ var currentCategory = "";
+ var widget = this;
+ widget.menu.bindings = $();
+ $.each(items, function(index, item) {
+ if (item.category && item.category !== currentCategory) {
+ ul.append("" + categories[item.category] + "");
+ currentCategory = item.category;
+ }
+ var li = widget._renderItemData(ul, item);
+ if (item.category) {
+ li.attr("aria-label", categories[item.category] + " : " + item.l);
+ } else {
+ li.attr("aria-label", item.l);
+ }
+ li.attr("class", "result-item");
+ });
+ ul.append("Go to search page");
+ },
+ _renderItem: function(ul, item) {
+ var li = $("").appendTo(ul);
+ var div = $("").appendTo(li);
+ var label = item.l
+ ? item.l
+ : getHighlightedText(item.input, item.boundaries, 0, item.input.length);
+ var idx = item.indexItem;
+ if (item.category === "searchTags" && idx && idx.h) {
+ if (idx.d) {
+ div.html(label + " (" + idx.h + ")
"
+ + idx.d + "
");
+ } else {
+ div.html(label + " (" + idx.h + ")");
+ }
+ } else {
+ div.html(label);
+ }
+ return li;
+ }
+});
+$(function() {
+ var expanded = false;
+ var windowWidth;
+ function collapse() {
+ if (expanded) {
+ $("div#navbar-top").removeAttr("style");
+ $("button#navbar-toggle-button")
+ .removeClass("expanded")
+ .attr("aria-expanded", "false");
+ expanded = false;
+ }
+ }
+ $("button#navbar-toggle-button").click(function (e) {
+ if (expanded) {
+ collapse();
+ } else {
+ var navbar = $("div#navbar-top");
+ navbar.height(navbar.prop("scrollHeight"));
+ $("button#navbar-toggle-button")
+ .addClass("expanded")
+ .attr("aria-expanded", "true");
+ expanded = true;
+ windowWidth = window.innerWidth;
+ }
+ });
+ $("ul.sub-nav-list-small li a").click(collapse);
+ $("input#search-input").focus(collapse);
+ $("main").click(collapse);
+ $("section[id] > :header, :header[id], :header:has(a[id])").each(function(idx, el) {
+ // Create anchor links for headers with an associated id attribute
+ var hdr = $(el);
+ var id = hdr.attr("id") || hdr.parent("section").attr("id") || hdr.children("a").attr("id");
+ if (id) {
+ hdr.append(" ");
+ }
+ });
+ $(window).on("orientationchange", collapse).on("resize", function(e) {
+ if (expanded && windowWidth !== window.innerWidth) collapse();
+ });
+ var search = $("#search-input");
+ var reset = $("#reset-button");
+ search.catcomplete({
+ minLength: 1,
+ delay: 200,
+ source: doSearch,
+ response: function(event, ui) {
+ if (!ui.content.length) {
+ ui.content.push({ l: messages.noResult });
+ } else {
+ $("#search-input").empty();
+ }
+ },
+ autoFocus: true,
+ focus: function(event, ui) {
+ return false;
+ },
+ position: {
+ collision: "flip"
+ },
+ select: function(event, ui) {
+ if (ui.item.indexItem) {
+ var url = getURL(ui.item.indexItem, ui.item.category);
+ window.location.href = pathtoroot + url;
+ $("#search-input").focus();
+ }
+ }
+ });
+ search.val('');
+ search.prop("disabled", false);
+ reset.prop("disabled", false);
+ reset.click(function() {
+ search.val('').focus();
+ });
+ search.focus();
+});
diff --git a/docs/search.html b/docs/search.html
new file mode 100644
index 0000000..496d80b
--- /dev/null
+++ b/docs/search.html
@@ -0,0 +1,68 @@
+
+
+
+
+Suchen
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Suchen
+
+
+
+
+Zusätzliche Ressourcen
+
+
+
+
Die Hilfeseite enthält eine Einführung in den Umfang und die Syntax der JavaDoc-Suche.
+
Sie können die <STRG>- oder <CMD>-Taste zusammen mit den Pfeiltasten nach links und rechts verwenden, um zwischen Ergebnisregisterkarten auf dieser Seite zu wechseln.
+
Mit der URL-Vorlage unten können Sie diese Seite als Suchmaschine in Browsern konfigurieren, die dieses Feature unterstützen. Das Feature wurde erfolgreich mit Google Chrome und Mozilla Firefox getestet. Beachten Sie, dass andere Browser dieses Feature möglicherweise nicht unterstützen oder ein anderes URL-Format erfordern.
+
link
+
+
+
+
+Suchindex wird geladen...
+
+
+
+
diff --git a/docs/tag-search-index.js b/docs/tag-search-index.js
new file mode 100644
index 0000000..0367dae
--- /dev/null
+++ b/docs/tag-search-index.js
@@ -0,0 +1 @@
+tagSearchIndex = [];updateSearchResults();
\ No newline at end of file
diff --git a/docs/type-search-index.js b/docs/type-search-index.js
new file mode 100644
index 0000000..ade97e5
--- /dev/null
+++ b/docs/type-search-index.js
@@ -0,0 +1 @@
+typeSearchIndex = [{"l":"Alle Klassen und Schnittstellen","u":"allclasses-index.html"},{"p":"me.micartey.yawen.json","l":"Asset"},{"p":"me.micartey.yawen.json","l":"Release"},{"p":"me.micartey.yawen","l":"YawenRepository"}];updateSearchResults();
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index c69a656..09dbaff 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
com.google.code.gson
gson
- 2.8.7
+ 2.9.1
diff --git a/src/main/java/me/micartey/yawen/YawenRepository.java b/src/main/java/me/micartey/yawen/YawenRepository.java
index 03321a0..22c4e43 100644
--- a/src/main/java/me/micartey/yawen/YawenRepository.java
+++ b/src/main/java/me/micartey/yawen/YawenRepository.java
@@ -2,18 +2,17 @@
import com.google.gson.Gson;
import io.vavr.control.Try;
-import lombok.Setter;
-import me.micartey.yawen.json.LatestRelease;
+import me.micartey.yawen.json.Asset;
+import me.micartey.yawen.json.Release;
import java.io.File;
import java.io.FileOutputStream;
-import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.*;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
public class YawenRepository {
@@ -27,7 +26,7 @@ public class YawenRepository {
* @param repository GitHub repository
*/
public YawenRepository(String repository) {
- this.apiUrl = String.format("https://api.github.com/repos/%s/releases/latest", repository);
+ this.apiUrl = String.format("https://api.github.com/repos/%s/releases", repository);
this.gson = new Gson();
}
@@ -50,109 +49,96 @@ private Try getWebsiteContent(String url) {
}
/**
- * Get the {@link LatestRelease latest release} in case a release is present
+ * Get the {@link Release latest release} in case a release is present
*
- * @return Optional of {@link LatestRelease LatestRelease}
+ * @return Optional of {@link Release LatestRelease}
* @see YawenRepository#getWebsiteContent(String)
*/
- public Optional getLatestRelease() {
- return this.getWebsiteContent(this.apiUrl)
- .mapTry(content -> Optional.of(this.gson.fromJson(content, LatestRelease.class)))
+ public Optional getLatestRelease() {
+ return this.getWebsiteContent(this.apiUrl + "/latest")
+ .mapTry(content -> Optional.of(this.gson.fromJson(content, Release.class)))
.getOrElse(Optional.empty());
}
+ /**
+ * Get all {@link Release releases} to filter through
+ *
+ * @return Set of releases
+ * @see YawenRepository#getWebsiteContent(String)
+ */
+ public Set getReleases() {
+ return this.getWebsiteContent(this.apiUrl)
+ .mapTry(content -> Arrays.stream(this.gson.fromJson(content, Release[].class)).collect(Collectors.toSet()))
+ .getOrElse(Collections.emptySet());
+ }
+
/**
* Collect the names of all jar assets
*
* @return Set of {@link String}
*/
- public Set getAssetNames() {
- List names = new ArrayList<>();
- this.getLatestRelease().ifPresent(release -> Arrays.stream(release.assetInfos)
- .filter(info -> info.name.endsWith(".jar"))
- .forEach(info -> {
- names.add(info.name);
- }));
- return new HashSet<>(names);
+ public Set getAssetNames(Release release) {
+ return Arrays.stream(release.assets).filter(info -> info.name.endsWith(".jar"))
+ .map(Asset::getName)
+ .collect(Collectors.toSet());
}
/**
- * Load an asset by name from the latest release
+ * Load an asset
*
- * @param name Name of jar file
+ * @param asset Asset
* @return Optional of {@link ClassLoader}
*/
- public Optional load(String name) {
- AtomicReference> reference = new AtomicReference<>(Optional.empty());
-
- this.getLatestRelease().flatMap(release -> Arrays.stream(release.assetInfos)
- .filter(info -> info.name.equals(name))
- .filter(info -> info.state.equals("uploaded"))
- .filter(info -> info.name.endsWith(".jar")).findFirst()).ifPresent(info -> {
- this.loadDependency(info.browserDownloadUrl).onSuccess(classLoader -> {
- reference.set(Optional.of(classLoader));
- });
- });
-
- return reference.get();
+ public Optional load(Asset asset) {
+ return this.loadDependency(asset.browserDownloadUrl).toJavaOptional();
}
/**
- * Load an asset by name from the latest release but cache it and update if needed
+ * Load an asset and cache it
*
- * @param name Name of jar file
+ * @param asset Asset
* @return Optional of {@link ClassLoader}
*/
- public Optional loadCached(String name) {
- AtomicReference> reference = new AtomicReference<>(Optional.empty());
+ public Optional loadCached(Asset asset) {
+ boolean update = Try.ofCallable(() -> {
+ File parent = new File(".yawen");
+ parent.mkdir();
- this.getLatestRelease().flatMap(release -> Arrays.stream(release.assetInfos)
- .filter(info -> info.name.equals(name))
- .filter(info -> info.state.equals("uploaded"))
- .filter(info -> info.name.endsWith(".jar")).findFirst()).ifPresent(info -> {
+ if (new File(parent, asset.id + ".jar").exists())
+ return false;
- boolean update = Try.ofCallable(() -> {
- File parent = new File(".yawen");
- parent.mkdir();
+ URL website = new URL(asset.browserDownloadUrl);
+ ReadableByteChannel byteChannel = Channels.newChannel(website.openStream());
- if (new File(parent, info.id + ".jar").exists())
- return false;
-
- URL website = new URL(info.browserDownloadUrl);
- ReadableByteChannel byteChannel = Channels.newChannel(website.openStream());
-
- try (FileOutputStream stream = new FileOutputStream(".yawen/" + info.id + ".jar")) {
- stream.getChannel().transferFrom(byteChannel, 0, Long.MAX_VALUE);
- }
-
- return true;
- }).get();
-
- // Logging might be unwanted - but will be added for debugging purposes
- if (update) {
- System.out.println("[yawen] Updated cache!");
+ try (FileOutputStream stream = new FileOutputStream(".yawen/" + asset.id + ".jar")) {
+ stream.getChannel().transferFrom(byteChannel, 0, Long.MAX_VALUE);
}
- this.loadDependency(new File(".yawen/" + info.id + ".jar")).onSuccess(classLoader -> {
- reference.set(Optional.of(classLoader));
- });
- });
+ return true;
+ }).get();
- return reference.get();
+ // Logging might be unwanted - but will be added for debugging purposes
+ if (update) {
+ System.out.println("[yawen] Updated cache!");
+ }
+
+ return this.loadDependency(new File(".yawen/" + asset.id + ".jar")).toJavaOptional();
}
/**
* Load an asset from the latest release
*
* @return Optional of {@link ClassLoader}
- * @see YawenRepository#load(String)
+ * @see YawenRepository#load(Asset)
*/
public Optional load() {
- for(String asset : this.getAssetNames()) {
- Optional classLoader = this.load(asset);
- if(classLoader.isPresent()) return classLoader;
- }
- return Optional.empty();
+ Release latest = this.getLatestRelease()
+ .orElseThrow(() -> new RuntimeException("No release found"));
+
+ Asset asset = Arrays.stream(latest.assets).findFirst()
+ .orElseThrow(() -> new RuntimeException("Latest release has no assets"));
+
+ return this.load(asset);
}
private Try loadDependency(String url) {
diff --git a/src/main/java/me/micartey/yawen/json/AssetInfo.java b/src/main/java/me/micartey/yawen/json/Asset.java
similarity index 61%
rename from src/main/java/me/micartey/yawen/json/AssetInfo.java
rename to src/main/java/me/micartey/yawen/json/Asset.java
index 06ded18..00f939b 100644
--- a/src/main/java/me/micartey/yawen/json/AssetInfo.java
+++ b/src/main/java/me/micartey/yawen/json/Asset.java
@@ -1,8 +1,10 @@
package me.micartey.yawen.json;
import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
-public class AssetInfo {
+@Getter
+public class Asset {
@SerializedName("browser_download_url")
public String browserDownloadUrl;
@@ -10,9 +12,15 @@ public class AssetInfo {
@SerializedName("download_count")
public int downloadCount;
+ @SerializedName("updated_at")
+ public String updated;
+
+ @SerializedName("state")
public String state;
+ @SerializedName("name")
public String name;
+ @SerializedName("id")
public String id;
}
diff --git a/src/main/java/me/micartey/yawen/json/LatestRelease.java b/src/main/java/me/micartey/yawen/json/Release.java
similarity index 69%
rename from src/main/java/me/micartey/yawen/json/LatestRelease.java
rename to src/main/java/me/micartey/yawen/json/Release.java
index aa380d1..f4c13ee 100644
--- a/src/main/java/me/micartey/yawen/json/LatestRelease.java
+++ b/src/main/java/me/micartey/yawen/json/Release.java
@@ -1,11 +1,13 @@
package me.micartey.yawen.json;
import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
-public class LatestRelease {
+@Getter
+public class Release {
@SerializedName("assets")
- public AssetInfo[] assetInfos;
+ public Asset[] assets;
@SerializedName("name")
public String releaseName;