diff --git a/common/src/main/java/io/xeres/common/id/Id.java b/common/src/main/java/io/xeres/common/id/Id.java index bb2b8431..5474e9bf 100644 --- a/common/src/main/java/io/xeres/common/id/Id.java +++ b/common/src/main/java/io/xeres/common/id/Id.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -115,10 +115,14 @@ public static String toStringLowerCase(long id) * Converts an identifier into its hexadecimal representation. * * @param identifier the identifier - * @return a hexadecimal lowercase representation of the identifier, without prefix + * @return a hexadecimal lowercase representation of the identifier, without prefix, or an empty string if the identifier is empty */ public static String toString(Identifier identifier) { + if (identifier == null) + { + return ""; + } return toString(identifier.getBytes()); } diff --git a/ui/src/main/java/io/xeres/ui/controller/chat/ChatViewController.java b/ui/src/main/java/io/xeres/ui/controller/chat/ChatViewController.java index d5a9341b..bb5b26b9 100644 --- a/ui/src/main/java/io/xeres/ui/controller/chat/ChatViewController.java +++ b/ui/src/main/java/io/xeres/ui/controller/chat/ChatViewController.java @@ -40,7 +40,6 @@ import io.xeres.ui.support.sound.SoundService.SoundType; import io.xeres.ui.support.tray.TrayService; import io.xeres.ui.support.uri.ChatRoomUri; -import io.xeres.ui.support.uri.ChatRoomUriFactory; import io.xeres.ui.support.uri.UriService; import io.xeres.ui.support.util.ImageUtils; import io.xeres.ui.support.util.TextInputControlUtils; @@ -310,7 +309,7 @@ private void createRoomTreeContextMenu() copyLinkItem.setGraphic(new FontIcon(MaterialDesignL.LINK_VARIANT)); copyLinkItem.setOnAction(event -> { var chatRoomInfo = ((RoomHolder) event.getSource()).getRoomInfo(); - ClipboardUtils.copyTextToClipboard(ChatRoomUriFactory.generate(chatRoomInfo.getName(), chatRoomInfo.getId())); + ClipboardUtils.copyTextToClipboard(new ChatRoomUri(chatRoomInfo.getName(), chatRoomInfo.getId()).toString()); }); var xContextMenu = new XContextMenu(subscribeItem, unsubscribeItem, new SeparatorMenuItem(), copyLinkItem); diff --git a/ui/src/main/java/io/xeres/ui/controller/file/FileResultView.java b/ui/src/main/java/io/xeres/ui/controller/file/FileResultView.java index 8c65c639..e6110cbc 100644 --- a/ui/src/main/java/io/xeres/ui/controller/file/FileResultView.java +++ b/ui/src/main/java/io/xeres/ui/controller/file/FileResultView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -25,7 +25,7 @@ import io.xeres.ui.client.FileClient; import io.xeres.ui.support.clipboard.ClipboardUtils; import io.xeres.ui.support.contextmenu.XContextMenu; -import io.xeres.ui.support.uri.FileUriFactory; +import io.xeres.ui.support.uri.FileUri; import javafx.application.Platform; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; @@ -173,7 +173,8 @@ private void createFilesTableViewContextMenu() copyLinkItem.setOnAction(event -> { if (event.getSource() instanceof FileResult file) { - ClipboardUtils.copyTextToClipboard(FileUriFactory.generate(file.name(), file.size(), Sha1Sum.fromString(file.hash()))); + var fileUri = new FileUri(file.name(), file.size(), Sha1Sum.fromString(file.hash())); + ClipboardUtils.copyTextToClipboard(fileUri.toString()); } }); diff --git a/ui/src/main/java/io/xeres/ui/controller/file/FileSearchViewController.java b/ui/src/main/java/io/xeres/ui/controller/file/FileSearchViewController.java index 650723b8..e7e7e44e 100644 --- a/ui/src/main/java/io/xeres/ui/controller/file/FileSearchViewController.java +++ b/ui/src/main/java/io/xeres/ui/controller/file/FileSearchViewController.java @@ -27,7 +27,6 @@ import io.xeres.ui.support.clipboard.ClipboardUtils; import io.xeres.ui.support.contextmenu.XContextMenu; import io.xeres.ui.support.uri.SearchUri; -import io.xeres.ui.support.uri.SearchUriFactory; import io.xeres.ui.support.util.TextInputControlUtils; import io.xeres.ui.support.util.UiUtils; import javafx.application.Platform; @@ -38,7 +37,6 @@ import javafx.scene.control.TextField; import javafx.scene.input.KeyCode; import net.rgielen.fxweaver.core.FxmlView; -import org.apache.commons.lang3.StringUtils; import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.materialdesign2.MaterialDesignL; import org.slf4j.Logger; @@ -158,7 +156,8 @@ private void createContextMenu() copyLinkItem.setGraphic(new FontIcon(MaterialDesignL.LINK_VARIANT)); copyLinkItem.setOnAction(event -> { var fileResultView = (FileResultView) event.getSource(); - ClipboardUtils.copyTextToClipboard(SearchUriFactory.generate(StringUtils.left(fileResultView.getText(), 50), fileResultView.getText())); + var searchUri = new SearchUri(fileResultView.getText()); + ClipboardUtils.copyTextToClipboard(searchUri.toString()); }); var xContextMenu = new XContextMenu(copyLinkItem); diff --git a/ui/src/main/java/io/xeres/ui/controller/forum/ForumViewController.java b/ui/src/main/java/io/xeres/ui/controller/forum/ForumViewController.java index bc1899f5..8ce3ea03 100644 --- a/ui/src/main/java/io/xeres/ui/controller/forum/ForumViewController.java +++ b/ui/src/main/java/io/xeres/ui/controller/forum/ForumViewController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 by David Gerber - https://zapek.com + * Copyright (c) 2023-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -42,7 +42,6 @@ import io.xeres.ui.support.markdown.MarkdownService.ParsingMode; import io.xeres.ui.support.preference.PreferenceUtils; import io.xeres.ui.support.uri.ForumUri; -import io.xeres.ui.support.uri.ForumUriFactory; import io.xeres.ui.support.uri.IdentityUri; import io.xeres.ui.support.uri.UriService; import io.xeres.ui.support.util.UiUtils; @@ -320,7 +319,8 @@ private void createForumTreeContextMenu() copyLinkItem.setGraphic(new FontIcon(MaterialDesignL.LINK_VARIANT)); copyLinkItem.setOnAction(event -> { var forumGroup = ((ForumGroup) event.getSource()); - ClipboardUtils.copyTextToClipboard(ForumUriFactory.generate(forumGroup.getName(), forumGroup.getGxsId())); + var forumUri = new ForumUri(forumGroup.getName(), forumGroup.getGxsId(), null); + ClipboardUtils.copyTextToClipboard(forumUri.toString()); }); var xContextMenu = new XContextMenu(subscribeItem, unsubscribeItem, new SeparatorMenuItem(), copyLinkItem); @@ -349,7 +349,8 @@ private void createForumMessageTableViewContextMenu() copyLinkItem.setGraphic(new FontIcon(MaterialDesignL.LINK_VARIANT)); copyLinkItem.setOnAction(event -> { @SuppressWarnings("unchecked") var forumMessage = ((TreeItem) event.getSource()).getValue(); - ClipboardUtils.copyTextToClipboard(ForumUriFactory.generate(forumMessage.getName(), forumMessage.getGxsId(), forumMessage.getMessageId())); + var forumUri = new ForumUri(forumMessage.getName(), forumMessage.getGxsId(), forumMessage.getMessageId()); + ClipboardUtils.copyTextToClipboard(forumUri.toString()); }); var xContextMenu = new XContextMenu(replyItem, new SeparatorMenuItem(), copyLinkItem); diff --git a/ui/src/main/java/io/xeres/ui/support/ImageCacheService.java b/ui/src/main/java/io/xeres/ui/support/ImageCacheService.java index d1bcf030..7bc18db0 100644 --- a/ui/src/main/java/io/xeres/ui/support/ImageCacheService.java +++ b/ui/src/main/java/io/xeres/ui/support/ImageCacheService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -63,7 +63,7 @@ public Image getImage(String url) @Override public void putImage(String url, Image image) { - if (url.startsWith("data:") || !isImageCacheable(image)) + if (!isUrlCacheable(url) || !isImageCacheable(image)) { return; } @@ -144,6 +144,11 @@ private boolean isImageCacheable(Image image) return maxSize > 0 && image.getWidth() * image.getHeight() * 4 < MAX_IMAGE_SIZE; } + private boolean isUrlCacheable(String url) + { + return !url.startsWith("data:"); + } + private static class ImageSizeSoftReference extends SoftReference { private final String url; diff --git a/ui/src/main/java/io/xeres/ui/support/contentline/ContentUri.java b/ui/src/main/java/io/xeres/ui/support/contentline/ContentUri.java index ccc88c3f..1e12c237 100644 --- a/ui/src/main/java/io/xeres/ui/support/contentline/ContentUri.java +++ b/ui/src/main/java/io/xeres/ui/support/contentline/ContentUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -21,37 +21,62 @@ import io.xeres.common.i18n.I18nUtils; import io.xeres.ui.custom.DisclosedHyperlink; +import io.xeres.ui.support.clipboard.ClipboardUtils; import io.xeres.ui.support.uri.UriService; import io.xeres.ui.support.util.UiUtils; +import javafx.event.ActionEvent; import javafx.scene.Node; +import javafx.scene.control.ContextMenu; import javafx.scene.control.Hyperlink; +import javafx.scene.control.MenuItem; +import org.kordamp.ikonli.javafx.FontIcon; +import org.kordamp.ikonli.materialdesign2.MaterialDesignC; import java.text.MessageFormat; +import java.util.Objects; import java.util.ResourceBundle; import java.util.function.Consumer; public class ContentUri implements Content { private final Hyperlink node; + private static final ContextMenu contextMenu; - private final ResourceBundle bundle = I18nUtils.getBundle(); + private static final ResourceBundle bundle = I18nUtils.getBundle(); + + static + { + var copyMenuItem = new MenuItem(bundle.getString("copy")); + copyMenuItem.setGraphic(new FontIcon(MaterialDesignC.CONTENT_COPY)); + copyMenuItem.setOnAction(ContentUri::copyToClipboard); + + contextMenu = new ContextMenu(copyMenuItem); + } public ContentUri(String uri) { node = new Hyperlink(uri); node.setOnAction(event -> UriService.openUri(appendMailToIfNeeded(node.getText()))); + initContextMenu(); } public ContentUri(String uri, String description) { node = new DisclosedHyperlink(description, uri); node.setOnAction(event -> askBeforeOpeningIfNeeded(() -> UriService.openUri(appendMailToIfNeeded(uri)))); + initContextMenu(); } public ContentUri(String uri, String description, Consumer action) { node = new DisclosedHyperlink(description, uri); node.setOnAction(event -> askBeforeOpeningIfNeeded(() -> action.accept(uri))); + initContextMenu(); + } + + private void initContextMenu() + { + node.setOnContextMenuRequested(event -> contextMenu.show(node, event.getScreenX(), event.getScreenY())); } private void askBeforeOpeningIfNeeded(Runnable action) @@ -82,7 +107,31 @@ public Node getNode() } public String getUri() + { + return getUri(node); + } + + @Override + public String asText() { return node.getText(); } + + private static String getUri(Node node) + { + return switch (node) + { + case DisclosedHyperlink disclosedHyperlink -> disclosedHyperlink.getUri(); + case Hyperlink hyperlink -> hyperlink.getText(); + default -> ""; + }; + } + + private static void copyToClipboard(ActionEvent event) + { + var selectedMenuItem = (MenuItem) event.getTarget(); + + var popup = Objects.requireNonNull(selectedMenuItem.getParentPopup()); + ClipboardUtils.copyTextToClipboard(getUri(popup.getOwnerNode())); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/markdown/UrlDetector.java b/ui/src/main/java/io/xeres/ui/support/markdown/UrlDetector.java index e4e9fb3e..50564cd0 100644 --- a/ui/src/main/java/io/xeres/ui/support/markdown/UrlDetector.java +++ b/ui/src/main/java/io/xeres/ui/support/markdown/UrlDetector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 by David Gerber - https://zapek.com + * Copyright (c) 2023-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -19,24 +19,24 @@ package io.xeres.ui.support.markdown; -import io.xeres.ui.support.contentline.ContentUri; +import io.xeres.ui.support.uri.UriFactory; import java.util.regex.Pattern; class UrlDetector implements MarkdownDetector { - private static final Pattern URL_PATTERN = Pattern.compile("\\b(?(?:https?|ftps?)://[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])|(?[0-9A-Z._+\\-=]+@[0-9a-z\\-]+\\.[a-z]{2,})", Pattern.CASE_INSENSITIVE); + private static final Pattern URL_PATTERN = Pattern.compile("\\b(?(?:https?|ftps?|retroshare)://[-A-Z0-9+&@#/%?=~_|!:,.;()]*[-A-Z0-9+&@#/%=~_|()])|(?[0-9A-Z._+\\-=]+@[0-9a-z\\-]+\\.[a-z]{2,})", Pattern.CASE_INSENSITIVE); @Override public boolean isPossibly(String line) { - return line.contains("http") || line.contains("ftp") || line.contains("@"); + return line.contains("http") || line.contains("ftp") || line.contains("@") || line.contains("retroshare"); } @Override public void process(Context context, String line) { MarkdownService.processPattern(URL_PATTERN, context, line, - (s, groupName) -> context.addContent(new ContentUri(s))); + (s, groupName) -> context.addContent(UriFactory.createContent(s, "", context.getUriAction()))); } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/AbstractUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/AbstractUriFactory.java index c0f58f80..34d4770d 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/AbstractUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/AbstractUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -22,14 +22,10 @@ import io.xeres.common.id.Sha1Sum; import io.xeres.ui.support.contentline.Content; import io.xeres.ui.support.markdown.UriAction; -import org.apache.commons.lang3.StringUtils; import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriUtils; import java.util.Locale; -import static java.nio.charset.StandardCharsets.UTF_8; - public abstract class AbstractUriFactory { protected static final String PROTOCOL_RETROSHARE = "retroshare"; @@ -43,39 +39,6 @@ public String getProtocol() return PROTOCOL_RETROSHARE; } - protected static String buildUri(String protocol, String authority, String... args) - { - var sb = new StringBuilder(protocol); - var firstArg = true; - - if (args.length % 2 != 0) - { - throw new IllegalArgumentException("Wrong number of arguments: must be name and value pairs"); - } - sb.append("://"); - sb.append(authority); - - for (var i = 0; i < args.length; i += 2) - { - if (StringUtils.isNotBlank(args[i + 1])) - { - if (firstArg) - { - sb.append("?"); - firstArg = false; - } - else - { - sb.append("&"); - } - sb.append(args[i]); - sb.append("="); - sb.append(UriUtils.encodeQueryParam(args[i + 1], UTF_8)); - } - } - return sb.toString(); - } - protected static long getLongHexArgument(String s) { try diff --git a/ui/src/main/java/io/xeres/ui/support/uri/BoardsUri.java b/ui/src/main/java/io/xeres/ui/support/uri/BoardsUri.java index daa56924..51601220 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/BoardsUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/BoardsUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -20,8 +20,23 @@ package io.xeres.ui.support.uri; import io.xeres.common.id.GxsId; +import io.xeres.common.id.Id; import io.xeres.common.id.MessageId; public record BoardsUri(String name, GxsId id, MessageId messageId) implements Uri { + static String AUTHORITY = "posted"; + + static String PARAMETER_NAME = "name"; + static String PARAMETER_ID = "id"; + static String PARAMETER_MSGID = "msgid"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_NAME, name, + PARAMETER_ID, Id.toString(id), + PARAMETER_MSGID, Id.toString(messageId)); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/BoardsUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/BoardsUriFactory.java index 12e07417..94cf74e5 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/BoardsUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/BoardsUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -30,14 +30,10 @@ import java.util.stream.Stream; +import static io.xeres.ui.support.uri.BoardsUri.*; + public class BoardsUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "posted"; - - private static final String PARAMETER_NAME = "name"; - private static final String PARAMETER_ID = "id"; - private static final String PARAMETER_MSGID = "msgid"; - @Override public String getAuthority() { @@ -58,16 +54,6 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var boardsUri = new BoardsUri(name, GxsId.fromString(boardId), StringUtils.isNotBlank(msgId) ? MessageId.fromString(msgId) : null); - return new ContentUri(msgId != null ? msgId : boardId, name, uri -> uriAction.openUri(boardsUri)); - } - - public static String generate(String name, String id, String msgId) - { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_NAME, name, - PARAMETER_ID, id, - PARAMETER_MSGID, msgId); - - return "" + name + ""; + return new ContentUri(boardsUri.toString(), StringUtils.isNotBlank(text) ? text : name, uri -> uriAction.openUri(boardsUri)); } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/CertificateUri.java b/ui/src/main/java/io/xeres/ui/support/uri/CertificateUri.java index 82230c9d..852acb43 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/CertificateUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/CertificateUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -21,4 +21,17 @@ public record CertificateUri(String radix, String name, String location) implements Uri { + static String AUTHORITY = "certificate"; + static String PARAMETER_RADIX = "radix"; + static String PARAMETER_NAME = "name"; + static String PARAMETER_LOCATION = "location"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_RADIX, radix, + PARAMETER_NAME, name, + PARAMETER_LOCATION, location); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/CertificateUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/CertificateUriFactory.java index a9847a22..2c73e67a 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/CertificateUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/CertificateUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -27,14 +27,10 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.web.util.UriComponents; +import static io.xeres.ui.support.uri.CertificateUri.*; + public class CertificateUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "certificate"; - - private static final String PARAMETER_RADIX = "radix"; - private static final String PARAMETER_NAME = "name"; - private static final String PARAMETER_LOCATION = "location"; - @Override public String getAuthority() { @@ -55,16 +51,42 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var certificateUri = new CertificateUri(radix, name, location); - return new ContentUri(StringUtils.defaultString(certificateUri.radix()), text, uri -> uriAction.openUri(certificateUri)); + return new ContentUri(certificateUri.toString(), StringUtils.isNotBlank(text) ? text : generateName(name, location), uri -> uriAction.openUri(certificateUri)); } - public static String generate(String radix, String name, String location) + private static String generateName(String name, String location) { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_RADIX, radix, - PARAMETER_NAME, name, - PARAMETER_LOCATION, location); + var sb = new StringBuilder(AppName.NAME); + sb.append(" Certificate ("); - return "" + AppName.NAME + " Certificate (" + name + ", @" + location + ")"; + if (StringUtils.isNotBlank(name)) + { + sb.append(name); + } + else + { + sb.append("unknown"); + } + if (StringUtils.isNotBlank(location)) + { + sb.append(", @"); + sb.append(location); + } + sb.append(")"); + return sb.toString(); + } + + /** + * Generates the certificate in a friendly way, since otherwise this would generate big URLs. + * + * @param radix the encoded certificate in base64 + * @param name the name + * @param location the location + * @return a link URL + */ + public static String generate(String radix, String name, String location) + { + var certificateUri = new CertificateUri(radix, name, location); + return "" + AppName.NAME + " Certificate (" + name + ", @" + location + ")"; } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/ChannelUri.java b/ui/src/main/java/io/xeres/ui/support/uri/ChannelUri.java index 4463d242..f2581d24 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/ChannelUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/ChannelUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -20,8 +20,23 @@ package io.xeres.ui.support.uri; import io.xeres.common.id.GxsId; +import io.xeres.common.id.Id; import io.xeres.common.id.MessageId; public record ChannelUri(String name, GxsId id, MessageId messageId) implements Uri { + static String AUTHORITY = "channel"; + + static String PARAMETER_NAME = "name"; + static String PARAMETER_ID = "id"; + static String PARAMETER_MSGID = "msgid"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_NAME, name, + PARAMETER_ID, Id.toString(id), + PARAMETER_MSGID, Id.toString(messageId)); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/ChannelUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/ChannelUriFactory.java index 0b509644..344e4eca 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/ChannelUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/ChannelUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -30,14 +30,10 @@ import java.util.stream.Stream; +import static io.xeres.ui.support.uri.ChannelUri.*; + public class ChannelUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "channel"; - - private static final String PARAMETER_NAME = "name"; - private static final String PARAMETER_ID = "id"; - private static final String PARAMETER_MSGID = "msgid"; - @Override public String getAuthority() { @@ -58,16 +54,6 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var channelUri = new ChannelUri(name, GxsId.fromString(id), StringUtils.isNotBlank(msgId) ? MessageId.fromString(msgId) : null); - return new ContentUri(msgId != null ? msgId : id, name, uri -> uriAction.openUri(channelUri)); - } - - public static String generate(String name, String id, String msgId) - { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_NAME, name, - PARAMETER_ID, id, - PARAMETER_MSGID, msgId); - - return "" + name + ""; + return new ContentUri(channelUri.toString(), StringUtils.isNotBlank(text) ? text : name, uri -> uriAction.openUri(channelUri)); } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/ChatRoomUri.java b/ui/src/main/java/io/xeres/ui/support/uri/ChatRoomUri.java index 0137a6e1..e25e9fd8 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/ChatRoomUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/ChatRoomUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -19,6 +19,23 @@ package io.xeres.ui.support.uri; +import io.xeres.common.id.Id; + public record ChatRoomUri(String name, long id) implements Uri { + static String AUTHORITY = "chat_room"; + static String PARAMETER_NAME = "name"; + static String PARAMETER_ID = "id"; + static String CHAT_ROOM_PREFIX = "L"; + static String PRIVATE_MESSAGE_PREFIX = "P"; + static String DISTANT_CHAT_PREFIX = "D"; + static String BROADCAST_PREFIX = "L"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_NAME, name, + PARAMETER_ID, CHAT_ROOM_PREFIX + Id.toString(id)); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/ChatRoomUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/ChatRoomUriFactory.java index 1ef29f8c..3b26ac69 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/ChatRoomUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/ChatRoomUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -19,7 +19,6 @@ package io.xeres.ui.support.uri; -import io.xeres.common.id.Id; import io.xeres.ui.support.contentline.Content; import io.xeres.ui.support.contentline.ContentText; import io.xeres.ui.support.contentline.ContentUri; @@ -29,18 +28,10 @@ import java.util.stream.Stream; +import static io.xeres.ui.support.uri.ChatRoomUri.*; + public class ChatRoomUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "chat_room"; - - private static final String PARAMETER_NAME = "name"; - private static final String PARAMETER_ID = "id"; - - private static final String CHAT_ROOM_PREFIX = "L"; - private static final String PRIVATE_MESSAGE_PREFIX = "P"; - private static final String DISTANT_CHAT_PREFIX = "D"; - private static final String BROADCAST_PREFIX = "L"; - @Override public String getAuthority() { @@ -60,15 +51,6 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var chatRoomUri = new ChatRoomUri(name, id.length() > 1 && id.startsWith(CHAT_ROOM_PREFIX) ? getLongHexArgument(id.substring(1)) : 0); - return new ContentUri(id, name, uri -> uriAction.openUri(chatRoomUri)); - } - - public static String generate(String name, long chatRoomId) - { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_NAME, name, - PARAMETER_ID, CHAT_ROOM_PREFIX + Id.toString(chatRoomId)); - - return "" + name + ""; + return new ContentUri(chatRoomUri.toString(), StringUtils.isNotBlank(text) ? text : name, uri -> uriAction.openUri(chatRoomUri)); } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/CollectionUri.java b/ui/src/main/java/io/xeres/ui/support/uri/CollectionUri.java index 741e452c..11df01a8 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/CollectionUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/CollectionUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -21,4 +21,20 @@ public record CollectionUri(String name, long size, String radix, int count) implements Uri { + static String AUTHORITY = "collection"; + + static String PARAMETER_NAME = "name"; + static String PARAMETER_SIZE = "size"; + static String PARAMETER_RADIX = "radix"; + static String PARAMETER_FILES = "files"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_NAME, name, + PARAMETER_SIZE, String.valueOf(size), + PARAMETER_RADIX, radix, + PARAMETER_FILES, String.valueOf(count)); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/CollectionUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/CollectionUriFactory.java index 74fceef1..912af2ee 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/CollectionUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/CollectionUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -29,15 +29,10 @@ import java.util.stream.Stream; +import static io.xeres.ui.support.uri.CollectionUri.*; + public class CollectionUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "collection"; - - private static final String PARAMETER_NAME = "name"; - private static final String PARAMETER_SIZE = "size"; - private static final String PARAMETER_RADIX = "radix"; - private static final String PARAMETER_FILES = "files"; - @Override public String getAuthority() { @@ -60,17 +55,6 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var collectionUri = new CollectionUri(name, getLongArgument(size), radix, getIntArgument(count)); //noinspection ConstantConditions - return new ContentUri(radix, name + " (" + count + "files, " + ByteUnitUtils.fromBytes(Long.parseLong(size)) + ")", uri -> uriAction.openUri(collectionUri)); - } - - public static String generate(String name, int size, String radix, String files) - { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_NAME, name, - PARAMETER_SIZE, String.valueOf(size), - PARAMETER_RADIX, radix, - PARAMETER_FILES, files); - - return "" + name + ""; + return new ContentUri(collectionUri.toString(), StringUtils.isNotBlank(text) ? text : (name + " (" + count + "files, " + ByteUnitUtils.fromBytes(Long.parseLong(size)) + ")"), uri -> uriAction.openUri(collectionUri)); } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/FileUri.java b/ui/src/main/java/io/xeres/ui/support/uri/FileUri.java index a3404963..4a059237 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/FileUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/FileUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -23,4 +23,18 @@ public record FileUri(String name, long size, Sha1Sum hash) implements Uri { + static String AUTHORITY = "file"; + + static String PARAMETER_NAME = "name"; + static String PARAMETER_SIZE = "size"; + static String PARAMETER_HASH = "hash"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_NAME, name, + PARAMETER_SIZE, String.valueOf(size), + PARAMETER_HASH, hash.toString()); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/FileUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/FileUriFactory.java index 4ee6c900..0999646c 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/FileUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/FileUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -30,14 +30,10 @@ import java.util.stream.Stream; +import static io.xeres.ui.support.uri.FileUri.*; + public class FileUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "file"; - - private static final String PARAMETER_NAME = "name"; - private static final String PARAMETER_SIZE = "size"; - private static final String PARAMETER_HASH = "hash"; - @Override public String getAuthority() { @@ -58,16 +54,13 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var fileUri = new FileUri(name, getLongArgument(size), getHashArgument(hash)); - return new ContentUri(fileUri.hash().toString(), fileUri.name() + " (" + ByteUnitUtils.fromBytes(fileUri.size()) + ")", uri -> uriAction.openUri(fileUri)); + return new ContentUri(fileUri.toString(), StringUtils.isNotBlank(text) ? text : (fileUri.name() + " (" + ByteUnitUtils.fromBytes(fileUri.size()) + ")"), uri -> uriAction.openUri(fileUri)); } public static String generate(String name, long size, Sha1Sum hash) { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_NAME, name, - PARAMETER_SIZE, String.valueOf(size), - PARAMETER_HASH, hash.toString()); + var fileUri = new FileUri(name, size, hash); - return "" + name + ""; + return "" + name + " (" + ByteUnitUtils.fromBytes(size) + ")" + ""; } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/ForumUri.java b/ui/src/main/java/io/xeres/ui/support/uri/ForumUri.java index ba570829..38e2b9fe 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/ForumUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/ForumUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -20,8 +20,23 @@ package io.xeres.ui.support.uri; import io.xeres.common.id.GxsId; +import io.xeres.common.id.Id; import io.xeres.common.id.MessageId; public record ForumUri(String name, GxsId id, MessageId messageId) implements Uri { + static String AUTHORITY = "forum"; + + static String PARAMETER_NAME = "name"; + static String PARAMETER_ID = "id"; + static String PARAMETER_MSGID = "msgid"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_NAME, name, + PARAMETER_ID, Id.toString(id), + PARAMETER_MSGID, Id.toString(messageId)); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/ForumUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/ForumUriFactory.java index 5cea954a..d6d43f93 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/ForumUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/ForumUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -30,14 +30,10 @@ import java.util.stream.Stream; +import static io.xeres.ui.support.uri.ForumUri.*; + public class ForumUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "forum"; - - private static final String PARAMETER_NAME = "name"; - private static final String PARAMETER_ID = "id"; - private static final String PARAMETER_MSGID = "msgid"; - @Override public String getAuthority() { @@ -58,25 +54,6 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var forumUri = new ForumUri(name, GxsId.fromString(id), StringUtils.isNotBlank(msgId) ? MessageId.fromString(msgId) : null); - return new ContentUri(msgId != null ? msgId : id, name, uri -> uriAction.openUri(forumUri)); - } - - public static String generate(String name, GxsId id, MessageId msgId) - { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_NAME, name, - PARAMETER_ID, id.toString(), - PARAMETER_MSGID, msgId.toString()); - - return "" + name + ""; - } - - public static String generate(String name, GxsId id) - { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_NAME, name, - PARAMETER_ID, id.toString()); - - return "" + name + ""; + return new ContentUri(forumUri.toString(), StringUtils.isNotBlank(text) ? text : name, uri -> uriAction.openUri(forumUri)); } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/IdentityUri.java b/ui/src/main/java/io/xeres/ui/support/uri/IdentityUri.java index c5f5b8b9..808c5d48 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/IdentityUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/IdentityUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -20,7 +20,22 @@ package io.xeres.ui.support.uri; import io.xeres.common.id.GxsId; +import io.xeres.common.id.Id; public record IdentityUri(String name, GxsId id, String groupData) implements Uri { + static String AUTHORITY = "identity"; + + static String PARAMETER_GXSID = "gxsid"; + static String PARAMETER_NAME = "name"; + static String PARAMETER_GROUPDATA = "groupdata"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_GXSID, Id.toString(id), + PARAMETER_NAME, name, + PARAMETER_GROUPDATA, groupData); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/IdentityUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/IdentityUriFactory.java index 14303f00..e74ee7e1 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/IdentityUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/IdentityUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -29,14 +29,10 @@ import java.util.stream.Stream; +import static io.xeres.ui.support.uri.IdentityUri.*; + public class IdentityUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "identity"; - - private static final String PARAMETER_GXSID = "gxsid"; - private static final String PARAMETER_NAME = "name"; - private static final String PARAMETER_GROUPDATA = "groupdata"; - @Override public String getAuthority() { @@ -57,16 +53,6 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var identityUri = new IdentityUri(name, GxsId.fromString(gxsId), groupData); // groupData contains the gxs group's data so that the peer can do something with it even if it doesn't have the group yet - return new ContentUri(groupData, "Identity (name=" + name + ", ID=" + gxsId + ")", uri -> uriAction.openUri(identityUri)); - } - - public static String generate(String gxsId, String name, String groupData) - { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_GXSID, gxsId, - PARAMETER_NAME, name, - PARAMETER_GROUPDATA, groupData); - - return "" + name + ""; + return new ContentUri(identityUri.toString(), StringUtils.isNotBlank(text) ? text : ("Identity (name=" + name + ", ID=" + gxsId + ")"), uri -> uriAction.openUri(identityUri)); } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/MessageUri.java b/ui/src/main/java/io/xeres/ui/support/uri/MessageUri.java index eebd4b19..c045e9ac 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/MessageUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/MessageUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -19,8 +19,21 @@ package io.xeres.ui.support.uri; +import io.xeres.common.id.Id; import io.xeres.common.id.Identifier; public record MessageUri(Identifier identifier, String subject) implements Uri { + static String AUTHORITY = "message"; + + static String PARAMETER_ID = "id"; + static String PARAMETER_SUBJECT = "subject"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_ID, Id.toString(identifier), + PARAMETER_SUBJECT, subject); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/MessageUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/MessageUriFactory.java index ef46b94c..0119a8c7 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/MessageUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/MessageUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -24,17 +24,14 @@ import io.xeres.ui.support.contentline.ContentText; import io.xeres.ui.support.contentline.ContentUri; import io.xeres.ui.support.markdown.UriAction; +import org.apache.commons.lang3.StringUtils; import org.springframework.web.util.UriComponents; +import static io.xeres.ui.support.uri.MessageUri.*; import static org.apache.commons.lang3.StringUtils.isBlank; public class MessageUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "message"; - - private static final String PARAMETER_ID = "id"; - private static final String PARAMETER_SUBJECT = "subject"; - @Override public String getAuthority() { @@ -54,15 +51,6 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var messageUri = new MessageUri(GxsId.fromString(id), subject); - return new ContentUri(id, id, uri -> uriAction.openUri(messageUri)); - } - - public static String generate(String id, String subject) - { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_ID, id, - PARAMETER_SUBJECT, subject); - - return "" + subject + ""; + return new ContentUri(messageUri.toString(), StringUtils.isNotBlank(text) ? text : id, uri -> uriAction.openUri(messageUri)); } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/ProfileUri.java b/ui/src/main/java/io/xeres/ui/support/uri/ProfileUri.java index d81a04f8..e45c8e78 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/ProfileUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/ProfileUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -19,6 +19,20 @@ package io.xeres.ui.support.uri; +import io.xeres.common.id.Id; + public record ProfileUri(String name, long hash) implements Uri { + static String AUTHORITY = "person"; + + static String PARAMETER_NAME = "name"; + static String PARAMETER_HASH = "hash"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_NAME, name, + PARAMETER_HASH, Id.toString(hash)); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/ProfileUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/ProfileUriFactory.java index 928d7a4c..22d1c968 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/ProfileUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/ProfileUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -28,13 +28,10 @@ import java.util.stream.Stream; +import static io.xeres.ui.support.uri.ProfileUri.*; + public class ProfileUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "person"; - - private static final String PARAMETER_NAME = "name"; - private static final String PARAMETER_HASH = "hash"; - @Override public String getAuthority() { @@ -54,15 +51,6 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var profileUri = new ProfileUri(name, getLongHexArgument(hash)); - return new ContentUri(hash, name + "@" + hash, uri -> uriAction.openUri(profileUri)); - } - - public static String generate(String name, String hash) - { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_NAME, name, - PARAMETER_HASH, hash); - - return "" + name + ""; + return new ContentUri(profileUri.toString(), StringUtils.isNotBlank(text) ? text : (name + "@" + hash), uri -> uriAction.openUri(profileUri)); } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/SearchUri.java b/ui/src/main/java/io/xeres/ui/support/uri/SearchUri.java index 021b9db2..3d1c60c9 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/SearchUri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/SearchUri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -21,4 +21,14 @@ public record SearchUri(String keywords) implements Uri { + static String AUTHORITY = "search"; + + static String PARAMETER_KEYWORDS = "keywords"; + + @Override + public String toString() + { + return Uri.buildUri(AUTHORITY, + PARAMETER_KEYWORDS, keywords); + } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/SearchUriFactory.java b/ui/src/main/java/io/xeres/ui/support/uri/SearchUriFactory.java index 155fd43c..2136860d 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/SearchUriFactory.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/SearchUriFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 by David Gerber - https://zapek.com + * Copyright (c) 2019-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -26,12 +26,11 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.web.util.UriComponents; +import static io.xeres.ui.support.uri.SearchUri.AUTHORITY; +import static io.xeres.ui.support.uri.SearchUri.PARAMETER_KEYWORDS; + public class SearchUriFactory extends AbstractUriFactory { - private static final String AUTHORITY = "search"; - - private static final String PARAMETER_KEYWORDS = "keywords"; - @Override public String getAuthority() { @@ -50,14 +49,6 @@ public Content create(UriComponents uriComponents, String text, UriAction uriAct var searchUri = new SearchUri(keywords.trim()); - return new ContentUri(keywords, keywords, uri -> uriAction.openUri(searchUri)); - } - - public static String generate(String name, String keywords) - { - var uri = buildUri(PROTOCOL_RETROSHARE, AUTHORITY, - PARAMETER_KEYWORDS, keywords); - - return "" + name + ""; + return new ContentUri(searchUri.toString(), StringUtils.isNotBlank(text) ? text : keywords, uri -> uriAction.openUri(searchUri)); } } diff --git a/ui/src/main/java/io/xeres/ui/support/uri/Uri.java b/ui/src/main/java/io/xeres/ui/support/uri/Uri.java index ceeb9d4b..4560b305 100644 --- a/ui/src/main/java/io/xeres/ui/support/uri/Uri.java +++ b/ui/src/main/java/io/xeres/ui/support/uri/Uri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 by David Gerber - https://zapek.com + * Copyright (c) 2024-2025 by David Gerber - https://zapek.com * * This file is part of Xeres. * @@ -19,6 +19,43 @@ package io.xeres.ui.support.uri; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.util.UriUtils; + +import static java.nio.charset.StandardCharsets.UTF_8; + public interface Uri { + static String buildUri(String authority, String... args) + { + var sb = new StringBuilder("retroshare"); + var firstArg = true; + + if (args.length % 2 != 0) + { + throw new IllegalArgumentException("Wrong number of arguments: must be name and value pairs"); + } + sb.append("://"); + sb.append(authority); + + for (var i = 0; i < args.length; i += 2) + { + if (StringUtils.isNotBlank(args[i + 1])) + { + if (firstArg) + { + sb.append("?"); + firstArg = false; + } + else + { + sb.append("&"); + } + sb.append(args[i]); + sb.append("="); + sb.append(UriUtils.encodeQueryParam(args[i + 1], UTF_8)); + } + } + return sb.toString(); + } } diff --git a/ui/src/test/java/io/xeres/ui/support/uri/BoardsUriFactoryTest.java b/ui/src/test/java/io/xeres/ui/support/uri/BoardsUriFactoryTest.java new file mode 100644 index 00000000..b8f4242d --- /dev/null +++ b/ui/src/test/java/io/xeres/ui/support/uri/BoardsUriFactoryTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025 by David Gerber - https://zapek.com + * + * This file is part of Xeres. + * + * Xeres is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Xeres 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Xeres. If not, see . + */ + +package io.xeres.ui.support.uri; + +import io.xeres.testutils.IdFakes; +import io.xeres.ui.support.contentline.ContentText; +import io.xeres.ui.support.contentline.ContentUri; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testfx.framework.junit5.ApplicationExtension; + +import static io.xeres.ui.support.uri.UriFactoryUtils.createUriComponentsFromUri; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +@ExtendWith(ApplicationExtension.class) +class BoardsUriFactoryTest +{ + @Test + void BoardsUri_WrongParams_MissingGxsId_Fail() + { + var url = "retroshare://posted?name=test"; + + var factory = new BoardsUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertInstanceOf(ContentText.class, content); + } + + @Test + void BoardsUri_WrongParams_MissingName_Fail() + { + var gxsId = IdFakes.createGxsId(); + var url = "retroshare://posted?id=" + gxsId; + + var factory = new BoardsUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertInstanceOf(ContentText.class, content); + } + + @Test + void BoardsUri_TwoParams_Success() + { + var gxsId = IdFakes.createGxsId(); + + var url = "retroshare://posted?name=test&id=" + gxsId; + + var factory = new BoardsUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals(url, ((ContentUri) content).getUri()); + } + + @Test + void BoardsUri_ThreeParams_Success() + { + var gxsId = IdFakes.createGxsId(); + var msgId = IdFakes.createMessageId(); + + var url = "retroshare://posted?name=test&id=" + gxsId + "&msgid=" + msgId; + + var factory = new BoardsUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals(url, ((ContentUri) content).getUri()); + } + + @Test + void BoardsUri_Pretty() + { + var gxsId = IdFakes.createGxsId(); + var msgId = IdFakes.createMessageId(); + + var url = "retroshare://posted?name=Fun%20Board&id=" + gxsId + "&msgid=" + msgId; + + var factory = new BoardsUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals("Fun Board", content.asText()); + } + + @Test + void BoardsUri_Pretty_FromText() + { + var gxsId = IdFakes.createGxsId(); + var msgId = IdFakes.createMessageId(); + + var url = "retroshare://posted?name=Fun%20Board&id=" + gxsId + "&msgid=" + msgId; + + var factory = new BoardsUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "Test", null); + + assertEquals("Test", content.asText()); + } +} \ No newline at end of file diff --git a/ui/src/test/java/io/xeres/ui/support/uri/CertificateUriFactoryTest.java b/ui/src/test/java/io/xeres/ui/support/uri/CertificateUriFactoryTest.java new file mode 100644 index 00000000..61386960 --- /dev/null +++ b/ui/src/test/java/io/xeres/ui/support/uri/CertificateUriFactoryTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2025 by David Gerber - https://zapek.com + * + * This file is part of Xeres. + * + * Xeres is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Xeres 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Xeres. If not, see . + */ + +package io.xeres.ui.support.uri; + +import io.xeres.ui.support.contentline.ContentText; +import io.xeres.ui.support.contentline.ContentUri; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testfx.framework.junit5.ApplicationExtension; + +import static io.xeres.ui.support.uri.UriFactoryUtils.createUriComponentsFromUri; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +@ExtendWith(ApplicationExtension.class) +class CertificateUriFactoryTest +{ + @Test + void CertificateUri_WrongParams_MissingRadix_Fail() + { + var url = "retroshare://certificate?name=foo"; + + var factory = new CertificateUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertInstanceOf(ContentText.class, content); + } + + @Test + void CertificateUri_OneParam_Success() + { + var url = "retroshare://certificate?radix=abcd0123"; + + var factory = new CertificateUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals(url, ((ContentUri) content).getUri()); + } + + @Test + void CertificateUri_TwoParams_Success() + { + var url = "retroshare://certificate?radix=abcd0123&name=foo"; + + var factory = new CertificateUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals(url, ((ContentUri) content).getUri()); + } + + @Test + void CertificateUri_ThreeParams_Success() + { + var url = "retroshare://certificate?radix=abcd0123&name=foo&location=earth"; + + var factory = new CertificateUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals(url, ((ContentUri) content).getUri()); + } + + @Test + void CertificateUri_Pretty() + { + var url = "retroshare://certificate?radix=abcd0123&name=foo&location=earth"; + + var factory = new CertificateUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals("Xeres Certificate (foo, @earth)", content.asText()); + } + + @Test + void CertificateUri_Pretty_FromText() + { + var url = "retroshare://certificate?radix=abcd0123&name=foo&location=earth"; + + var factory = new CertificateUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "Test", null); + + assertEquals("Test", content.asText()); + } + + @Test + void CertificateUri_Pretty_WithoutLocation() + { + var url = "retroshare://certificate?radix=abcd0123&name=foo"; + + var factory = new CertificateUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals("Xeres Certificate (foo)", content.asText()); + } + + @Test + void CertificateUri_Pretty_NoName() + { + var url = "retroshare://certificate?radix=abcd0123"; + + var factory = new CertificateUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals("Xeres Certificate (unknown)", content.asText()); + } + + @Test + void CertificateUri_Generate() + { + assertEquals("Xeres Certificate (foo, @bar)", CertificateUriFactory.generate("1234", "foo", "bar")); + } +} \ No newline at end of file diff --git a/ui/src/test/java/io/xeres/ui/support/uri/FileUriFactoryTest.java b/ui/src/test/java/io/xeres/ui/support/uri/FileUriFactoryTest.java index 72650003..b4547d40 100644 --- a/ui/src/test/java/io/xeres/ui/support/uri/FileUriFactoryTest.java +++ b/ui/src/test/java/io/xeres/ui/support/uri/FileUriFactoryTest.java @@ -1,19 +1,111 @@ +/* + * Copyright (c) 2025 by David Gerber - https://zapek.com + * + * This file is part of Xeres. + * + * Xeres is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Xeres 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Xeres. If not, see . + */ + package io.xeres.ui.support.uri; import io.xeres.testutils.Sha1SumFakes; +import io.xeres.ui.support.contentline.ContentText; +import io.xeres.ui.support.contentline.ContentUri; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testfx.framework.junit5.ApplicationExtension; +import static io.xeres.ui.support.uri.UriFactoryUtils.createUriComponentsFromUri; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +@ExtendWith(ApplicationExtension.class) class FileUriFactoryTest { @Test - void Generate_Success() + void FileUri_WrongParams_MissingName_Fail() + { + var url = "retroshare://file?size=128&hash=123400000000000000000000000000000000789a"; + + var factory = new FileUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertInstanceOf(ContentText.class, content); + } + + @Test + void FileUri_WrongParams_MissingSize_Fail() + { + var url = "retroshare://file?name=foo&hash=123400000000000000000000000000000000789a"; + + var factory = new FileUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertInstanceOf(ContentText.class, content); + } + + @Test + void FileUri_WrongParams_MissingHash_Fail() + { + var url = "retroshare://file?name=foo&size=128"; + + var factory = new FileUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertInstanceOf(ContentText.class, content); + } + + @Test + void FileUri_Success() + { + var url = "retroshare://file?name=foo&size=128&hash=123400000000000000000000000000000000789a"; + + var factory = new FileUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals(url, ((ContentUri) content).getUri()); + } + + @Test + void FileUri_Pretty() + { + var url = "retroshare://file?name=foo&size=128&hash=123400000000000000000000000000000000789a"; + + var factory = new FileUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "", null); + + assertEquals("foo (128 bytes)", content.asText()); + } + + @Test + void FileUri_Pretty_FromText() + { + var url = "retroshare://file?name=foo&size=128&hash=123400000000000000000000000000000000789a"; + + var factory = new FileUriFactory(); + var content = factory.create(createUriComponentsFromUri(url), "Test", null); + + assertEquals("Test", content.asText()); + } + + @Test + void FileUri_Generate_Success() { var hash = Sha1SumFakes.createSha1Sum(); var result = FileUriFactory.generate("foo", 128, hash); - assertEquals("foo", result); + assertEquals("foo (128 bytes)", result); } -} \ No newline at end of file +} diff --git a/ui/src/test/java/io/xeres/ui/support/uri/UriFactoryUtils.java b/ui/src/test/java/io/xeres/ui/support/uri/UriFactoryUtils.java new file mode 100644 index 00000000..7c130816 --- /dev/null +++ b/ui/src/test/java/io/xeres/ui/support/uri/UriFactoryUtils.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 by David Gerber - https://zapek.com + * + * This file is part of Xeres. + * + * Xeres is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Xeres 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Xeres. If not, see . + */ + +package io.xeres.ui.support.uri; + +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.net.URISyntaxException; + +public final class UriFactoryUtils +{ + private UriFactoryUtils() + { + throw new UnsupportedOperationException("Utility class"); + } + + public static UriComponents createUriComponentsFromUri(String url) + { + URI uri; + try + { + uri = new URI(url); + } + catch (URISyntaxException e) + { + throw new RuntimeException(e); + } + return UriComponentsBuilder.fromPath(uri.getPath()) + .query(uri.getQuery()) + .build(); + } +}