diff --git a/jabref b/jabref new file mode 160000 index 00000000000..bbb88ccd377 --- /dev/null +++ b/jabref @@ -0,0 +1 @@ +Subproject commit bbb88ccd377c754e70c6d92a818db00d43bd6d09 diff --git a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/BibLatexFormatter.java b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/BibLatexFormatter.java index 573a7e36c2f..85660a66f7b 100644 --- a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/BibLatexFormatter.java +++ b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/BibLatexFormatter.java @@ -1,3 +1,5 @@ + + package org.jabref.http.server.cayw.format; import java.util.List; @@ -14,8 +16,8 @@ public class BibLatexFormatter implements CAYWFormatter { @Override - public String getFormatName() { - return "biblatex"; + public List getFormatNames() { + return List.of("biblatex"); } @Override @@ -26,11 +28,9 @@ public MediaType getMediaType() { @Override public String format(CAYWQueryParams queryParams, List caywEntries) { String command = queryParams.getCommand(); - List bibEntries = caywEntries.stream() - .map(CAYWEntry::bibEntry) - .toList(); - + .map(CAYWEntry::bibEntry) + .toList(); return "\\%s{%s}".formatted(command, bibEntries.stream() .map(entry -> entry.getCitationKey().orElse("")) diff --git a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/CAYWFormatter.java b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/CAYWFormatter.java index ed7d652a383..212d94dbf8c 100644 --- a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/CAYWFormatter.java +++ b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/CAYWFormatter.java @@ -9,7 +9,11 @@ public interface CAYWFormatter { - String getFormatName(); + List getFormatNames(); + + default String getFormatName() { + return getFormatNames().get(0); + } MediaType getMediaType(); diff --git a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/CitepFormatter.java b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/CitepFormatter.java new file mode 100644 index 00000000000..f01b10619b4 --- /dev/null +++ b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/CitepFormatter.java @@ -0,0 +1,36 @@ +package org.jabref.http.server.cayw.format; + +import java.util.List; +import java.util.stream.Collectors; + +import org.jabref.http.server.cayw.CAYWQueryParams; +import org.jabref.http.server.cayw.gui.CAYWEntry; +import org.jabref.model.entry.BibEntry; + +import jakarta.ws.rs.core.MediaType; +import org.jvnet.hk2.annotations.Service; + +@Service +public class CitepFormatter implements CAYWFormatter { + + @Override + public List getFormatNames() { + return List.of("citep", "cite"); + } + + @Override + public MediaType getMediaType() { + return MediaType.TEXT_PLAIN_TYPE; + } + + @Override + public String format(CAYWQueryParams queryParams, List caywEntries) { + List bibEntries = caywEntries.stream() + .map(CAYWEntry::bibEntry) + .toList(); + String joined = bibEntries.stream() + .map(entry -> entry.getCitationKey().orElse("")) + .collect(Collectors.joining(",")); + return "\\citep{" + joined + "}"; + } +} diff --git a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/FormatterService.java b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/FormatterService.java index 4cb608d6196..de7c6545189 100644 --- a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/FormatterService.java +++ b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/FormatterService.java @@ -9,7 +9,6 @@ @Service public class FormatterService { - private static final String DEFAULT_FORMATTER = "biblatex"; private final Map formatters; @@ -17,13 +16,25 @@ public FormatterService() { this.formatters = new HashMap<>(); registerFormatter(new SimpleJsonFormatter()); registerFormatter(new BibLatexFormatter()); + registerFormatter(new NatbibFormatter()); + registerFormatter(new LatexFormatter()); + registerFormatter(new CitepFormatter()); + registerFormatter(new MmdFormatter()); + registerFormatter(new PandocFormatter()); + registerFormatter(new TypstFormatter()); } public void registerFormatter(CAYWFormatter formatter) { - formatters.putIfAbsent(formatter.getFormatName(), formatter); + for (String name : formatter.getFormatNames()) { + formatters.putIfAbsent(name.toLowerCase(), formatter); + } } public CAYWFormatter getFormatter(CAYWQueryParams queryParams) { - return formatters.getOrDefault(queryParams.getFormat().toLowerCase(), formatters.get(DEFAULT_FORMATTER)); + String format = queryParams.getFormat(); + if (format == null) { + return formatters.get(DEFAULT_FORMATTER); + } + return formatters.getOrDefault(format.toLowerCase(), formatters.get(DEFAULT_FORMATTER)); } } diff --git a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/LatexFormatter.java b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/LatexFormatter.java new file mode 100644 index 00000000000..56d0a17bbd4 --- /dev/null +++ b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/LatexFormatter.java @@ -0,0 +1,37 @@ +package org.jabref.http.server.cayw.format; + +import java.util.List; +import java.util.stream.Collectors; + +import org.jabref.http.server.cayw.CAYWQueryParams; +import org.jabref.http.server.cayw.gui.CAYWEntry; +import org.jabref.model.entry.BibEntry; + +import jakarta.ws.rs.core.MediaType; +import org.jvnet.hk2.annotations.Service; + +@Service +public class LatexFormatter implements CAYWFormatter { + + @Override + public List getFormatNames() { + return List.of("latex", "tex"); + } + + @Override + public MediaType getMediaType() { + return MediaType.TEXT_PLAIN_TYPE; + } + + @Override + public String format(CAYWQueryParams queryParams, List caywEntries) { + String command = queryParams.getCommand() != null ? queryParams.getCommand() : "autocite"; + List bibEntries = caywEntries.stream() + .map(CAYWEntry::bibEntry) + .toList(); + return "\\%s{%s}".formatted(command, + bibEntries.stream() + .map(entry -> entry.getCitationKey().orElse("")) + .collect(Collectors.joining(","))); + } +} diff --git a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/MmdFormatter.java b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/MmdFormatter.java new file mode 100644 index 00000000000..867c040befc --- /dev/null +++ b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/MmdFormatter.java @@ -0,0 +1,35 @@ +package org.jabref.http.server.cayw.format; + +import java.util.List; +import java.util.stream.Collectors; + +import org.jabref.http.server.cayw.CAYWQueryParams; +import org.jabref.http.server.cayw.gui.CAYWEntry; +import org.jabref.model.entry.BibEntry; + +import jakarta.ws.rs.core.MediaType; +import org.jvnet.hk2.annotations.Service; + +@Service +public class MmdFormatter implements CAYWFormatter { + + @Override + public List getFormatNames() { + return List.of("mmd", "multimarkdown"); + } + + @Override + public MediaType getMediaType() { + return MediaType.TEXT_PLAIN_TYPE; + } + + @Override + public String format(CAYWQueryParams queryParams, List caywEntries) { + List bibEntries = caywEntries.stream() + .map(CAYWEntry::bibEntry) + .toList(); + return bibEntries.stream() + .map(bibEntry -> "[@" + bibEntry.getCitationKey().orElse("") + "]") + .collect(Collectors.joining("")); + } +} diff --git a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/NatbibFormatter.java b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/NatbibFormatter.java new file mode 100644 index 00000000000..932f459279c --- /dev/null +++ b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/NatbibFormatter.java @@ -0,0 +1,53 @@ +package org.jabref.http.server.cayw.format; + +import java.util.List; +import java.util.stream.Collectors; + +import org.jabref.http.server.cayw.CAYWQueryParams; +import org.jabref.http.server.cayw.gui.CAYWEntry; +import org.jabref.model.entry.BibEntry; + +import jakarta.ws.rs.core.MediaType; +import org.jvnet.hk2.annotations.Service; + +@Service +public class NatbibFormatter implements CAYWFormatter { + + @Override + public List getFormatNames() { + return List.of("natbib", "nat"); + } + + @Override + public MediaType getMediaType() { + return MediaType.TEXT_PLAIN_TYPE; + } + + @Override + public String format(CAYWQueryParams queryParams, List caywEntries) { + String command = queryParams.getCommand(); + if (command == null) { + command = "citep"; + } + + command = mapToNatbibCommand(command); + + List bibEntries = caywEntries.stream() + .map(CAYWEntry::bibEntry) + .toList(); + + return "\\%s{%s}".formatted(command, + bibEntries.stream() + .map(entry -> entry.getCitationKey().orElse("")) + .collect(Collectors.joining(","))); + } + + private String mapToNatbibCommand(String command) { + return switch (command) { + case "author" -> "citeauthor"; + case "textcite" -> "citet"; + case "year" -> "citeyear"; + default -> command; + }; + } +} diff --git a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/PandocFormatter.java b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/PandocFormatter.java new file mode 100644 index 00000000000..cf6d507b88f --- /dev/null +++ b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/PandocFormatter.java @@ -0,0 +1,43 @@ +package org.jabref.http.server.cayw.format; + +import java.util.List; +import java.util.stream.Collectors; + +import org.jabref.http.server.cayw.CAYWQueryParams; +import org.jabref.http.server.cayw.gui.CAYWEntry; +import org.jabref.model.entry.BibEntry; + +import jakarta.ws.rs.core.MediaType; +import org.jvnet.hk2.annotations.Service; + +@Service +public class PandocFormatter implements CAYWFormatter { + @Override + public List getFormatNames() { + return List.of("pandoc", "markdown"); + } + + @Override + public MediaType getMediaType() { + return MediaType.TEXT_PLAIN_TYPE; + } + + @Override + public String format(CAYWQueryParams queryParams, List caywEntries) { + String command = queryParams.getCommand(); + List bibEntries = caywEntries.stream() + .map(CAYWEntry::bibEntry) + .toList(); + // Handle "parencite" command with square brackets + if ("parencite".equalsIgnoreCase(command)) { + return bibEntries.stream() + .map(bibEntry -> "[@" + bibEntry.getCitationKey().orElse("") + "]") + .collect(Collectors.joining("")); + } else { + // Default: bare @key format with semicolon separator + return bibEntries.stream() + .map(bibEntry -> "@" + bibEntry.getCitationKey().orElse("")) + .collect(Collectors.joining("; ")); + } + } +} diff --git a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/SimpleJsonFormatter.java b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/SimpleJsonFormatter.java index 480df4becfa..a74247aebb6 100644 --- a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/SimpleJsonFormatter.java +++ b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/SimpleJsonFormatter.java @@ -13,7 +13,6 @@ @Service public class SimpleJsonFormatter implements CAYWFormatter { - private final Gson gson; public SimpleJsonFormatter() { @@ -21,8 +20,8 @@ public SimpleJsonFormatter() { } @Override - public String getFormatName() { - return "simple-json"; + public List getFormatNames() { + return List.of("simple-json"); } @Override @@ -33,8 +32,8 @@ public MediaType getMediaType() { @Override public String format(CAYWQueryParams queryParams, List caywEntries) { List simpleJsons = caywEntries.stream() - .map(caywEntry -> SimpleJson.fromBibEntry(caywEntry.bibEntry())) - .toList(); + .map(caywEntry -> SimpleJson.fromBibEntry(caywEntry.bibEntry())) + .toList(); return gson.toJson(simpleJsons); } } diff --git a/jabsrv/src/main/java/org/jabref/http/server/cayw/format/TypstFormatter.java b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/TypstFormatter.java new file mode 100644 index 00000000000..feb11afc28c --- /dev/null +++ b/jabsrv/src/main/java/org/jabref/http/server/cayw/format/TypstFormatter.java @@ -0,0 +1,35 @@ +package org.jabref.http.server.cayw.format; + +import java.util.List; +import java.util.stream.Collectors; + +import org.jabref.http.server.cayw.CAYWQueryParams; +import org.jabref.http.server.cayw.gui.CAYWEntry; +import org.jabref.model.entry.BibEntry; + +import jakarta.ws.rs.core.MediaType; +import org.jvnet.hk2.annotations.Service; + +@Service +public class TypstFormatter implements CAYWFormatter { + + @Override + public List getFormatNames() { + return List.of("typst", "typ"); + } + + @Override + public MediaType getMediaType() { + return MediaType.TEXT_PLAIN_TYPE; + } + + @Override + public String format(CAYWQueryParams queryParams, List caywEntries) { + List bibEntries = caywEntries.stream() + .map(CAYWEntry::bibEntry) + .toList(); + return bibEntries.stream() + .map(bibEntry -> "@" + bibEntry.getCitationKey().orElse("")) + .collect(Collectors.joining(", ")); + } +} diff --git a/jabsrv/src/test/java/org/jabref/http/server/cayw/format/CitepFormatterTest.java b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/CitepFormatterTest.java new file mode 100644 index 00000000000..f2cf76de613 --- /dev/null +++ b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/CitepFormatterTest.java @@ -0,0 +1,46 @@ +package org.jabref.http.server.cayw.format; + +import java.util.List; +import java.util.Optional; + +import org.jabref.http.server.cayw.CAYWQueryParams; +import org.jabref.http.server.cayw.gui.CAYWEntry; +import org.jabref.model.entry.BibEntry; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +final class CitepFormatterTest { + + private CAYWEntry stubEntry(String key) { + BibEntry bib = mock(BibEntry.class); + when(bib.getCitationKey()).thenReturn(Optional.of(key)); + + CAYWEntry cayw = mock(CAYWEntry.class); + when(cayw.bibEntry()).thenReturn(bib); + return cayw; + } + + @Test + @DisplayName("getFormatNames() returns the two expected aliases in order") + void testGetFormatNames() { + CitepFormatter f = new CitepFormatter(); + assertEquals(List.of("citep", "cite"), f.getFormatNames()); + } + + @Test + @DisplayName("format() produces \\citep{key1,key2}") + void testFormat() { + CitepFormatter f = new CitepFormatter(); + + CAYWQueryParams qp = mock(CAYWQueryParams.class); // command is ignored + List entries = List.of(stubEntry("key1"), stubEntry("key2")); + + String out = f.format(qp, entries); + assertEquals("\\citep{key1,key2}", out); + } +} diff --git a/jabsrv/src/test/java/org/jabref/http/server/cayw/format/LatexFormatterTest.java b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/LatexFormatterTest.java new file mode 100644 index 00000000000..1f0eb77f53e --- /dev/null +++ b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/LatexFormatterTest.java @@ -0,0 +1,20 @@ +package org.jabref.http.server.cayw.format; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +final class LatexFormatterTest { + + @Test + void testGetFormatNamesNotEmpty() { + LatexFormatter f = new LatexFormatter(); + assertFalse(f.getFormatNames().isEmpty(), "Format-name list must not be empty"); + } + + @Test + void testMediaTypeIsTextPlain() { + assertEquals("text/plain", new LatexFormatter().getMediaType().toString()); + } +} diff --git a/jabsrv/src/test/java/org/jabref/http/server/cayw/format/MmdFormatterTest.java b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/MmdFormatterTest.java new file mode 100644 index 00000000000..c7bbfbaa107 --- /dev/null +++ b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/MmdFormatterTest.java @@ -0,0 +1,23 @@ +package org.jabref.http.server.cayw.format; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +final class MmdFormatterTest { + + @Test + void testGetFormatNamesNotEmpty() { + assertFalse(new MmdFormatter().getFormatNames().isEmpty()); + } + + @Test + void testAliasesUnique() { + List names = new MmdFormatter().getFormatNames(); + assertEquals(names.size(), names.stream().distinct().count(), + "Aliases must be unique"); + } +} diff --git a/jabsrv/src/test/java/org/jabref/http/server/cayw/format/NatbibFormatterTest.java b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/NatbibFormatterTest.java new file mode 100644 index 00000000000..651253cfb0c --- /dev/null +++ b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/NatbibFormatterTest.java @@ -0,0 +1,14 @@ +package org.jabref.http.server.cayw.format; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +final class NatbibFormatterTest { + + @Test + void testHasAtLeastTwoAliases() { + assertTrue(new NatbibFormatter().getFormatNames().size() >= 2, + "Natbib formatter should expose ≥ 3 aliases"); + } +} diff --git a/jabsrv/src/test/java/org/jabref/http/server/cayw/format/PandocFormatterTest.java b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/PandocFormatterTest.java new file mode 100644 index 00000000000..4f07b11745c --- /dev/null +++ b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/PandocFormatterTest.java @@ -0,0 +1,57 @@ +package org.jabref.http.server.cayw.format; + +import java.util.List; +import java.util.Optional; + +import org.jabref.http.server.cayw.CAYWQueryParams; +import org.jabref.http.server.cayw.gui.CAYWEntry; +import org.jabref.model.entry.BibEntry; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +final class PandocFormatterTest { + + private CAYWEntry stubEntry(String key) { + BibEntry bib = mock(BibEntry.class); + when(bib.getCitationKey()).thenReturn(Optional.of(key)); + + CAYWEntry cayw = mock(CAYWEntry.class); + when(cayw.bibEntry()).thenReturn(bib); + return cayw; + } + + @Test + void testGetFormatNames() { + PandocFormatter f = new PandocFormatter(); + assertEquals(List.of("pandoc", "markdown"), f.getFormatNames()); + } + + @Test + @DisplayName("Default command ⇒ '@k1; @k2'") + void testDefaultFormat() { + PandocFormatter f = new PandocFormatter(); + + CAYWQueryParams qp = mock(CAYWQueryParams.class); + when(qp.getCommand()).thenReturn(null); + + String out = f.format(qp, List.of(stubEntry("k1"), stubEntry("k2"))); + assertEquals("@k1; @k2", out); + } + + @Test + @DisplayName("parencite command ⇒ '[@k1][@k2]…'") + void testParenciteFormat() { + PandocFormatter f = new PandocFormatter(); + + CAYWQueryParams qp = mock(CAYWQueryParams.class); + when(qp.getCommand()).thenReturn("parencite"); + + String out = f.format(qp, List.of(stubEntry("k1"), stubEntry("k2"))); + assertEquals("[@k1][@k2]", out); + } +} diff --git a/jabsrv/src/test/java/org/jabref/http/server/cayw/format/TypstFormatterTest.java b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/TypstFormatterTest.java new file mode 100644 index 00000000000..acbc39100b1 --- /dev/null +++ b/jabsrv/src/test/java/org/jabref/http/server/cayw/format/TypstFormatterTest.java @@ -0,0 +1,13 @@ +package org.jabref.http.server.cayw.format; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +final class TypstFormatterTest { + + @Test + void testFirstAliasIsTypst() { + assertEquals("typst", new TypstFormatter().getFormatNames().get(0)); + } +}