3
3
import java .io .IOException ;
4
4
import java .io .InputStream ;
5
5
import java .nio .file .Files ;
6
+ import java .util .ArrayList ;
6
7
import java .util .List ;
7
8
import java .util .Objects ;
8
9
9
10
import org .jabref .http .JabrefMediaType ;
10
11
import org .jabref .http .dto .BibEntryDTO ;
12
+ import org .jabref .http .dto .LinkedPdfFileDTO ;
11
13
import org .jabref .http .server .services .ContextsToServe ;
12
14
import org .jabref .http .server .services .FilesToServe ;
13
15
import org .jabref .http .server .services .ServerUtils ;
14
16
import org .jabref .logic .citationstyle .JabRefItemDataProvider ;
15
17
import org .jabref .logic .preferences .CliPreferences ;
16
18
import org .jabref .model .database .BibDatabase ;
17
19
import org .jabref .model .database .BibDatabaseContext ;
20
+ import org .jabref .model .entry .BibEntry ;
18
21
import org .jabref .model .entry .BibEntryTypesManager ;
22
+ import org .jabref .model .entry .LinkedFile ;
23
+ import org .jabref .model .entry .field .StandardField ;
19
24
20
25
import com .airhacks .afterburner .injection .Injector ;
21
26
import com .google .gson .Gson ;
22
27
import jakarta .inject .Inject ;
28
+ import jakarta .ws .rs .Consumes ;
23
29
import jakarta .ws .rs .GET ;
24
30
import jakarta .ws .rs .InternalServerErrorException ;
31
+ import jakarta .ws .rs .NotFoundException ;
32
+ import jakarta .ws .rs .PUT ;
25
33
import jakarta .ws .rs .Path ;
26
34
import jakarta .ws .rs .PathParam ;
27
35
import jakarta .ws .rs .Produces ;
@@ -48,18 +56,94 @@ public class LibraryResource {
48
56
@ Inject
49
57
Gson gson ;
50
58
59
+ /**
60
+ * At http://localhost:23119/libraries/{id}
61
+ *
62
+ * @param id The specified library
63
+ * @return specified library in JSON format
64
+ * @throws IOException
65
+ */
51
66
@ GET
52
67
@ Produces (MediaType .APPLICATION_JSON )
53
68
public String getJson (@ PathParam ("id" ) String id ) throws IOException {
54
69
BibDatabaseContext databaseContext = getDatabaseContext (id );
55
70
BibEntryTypesManager entryTypesManager = Injector .instantiateModelOrService (BibEntryTypesManager .class );
56
71
List <BibEntryDTO > list = databaseContext .getDatabase ().getEntries ().stream ()
57
- .peek (bibEntry -> bibEntry .getSharedBibEntryData ().setSharedID (Objects .hash (bibEntry )))
58
- .map (entry -> new BibEntryDTO (entry , databaseContext .getMode (), preferences .getFieldPreferences (), entryTypesManager ))
59
- .toList ();
72
+ .peek (bibEntry -> bibEntry .getSharedBibEntryData ().setSharedID (Objects .hash (bibEntry )))
73
+ .map (entry -> new BibEntryDTO (entry , databaseContext .getMode (), preferences .getFieldPreferences (), entryTypesManager ))
74
+ .toList ();
60
75
return gson .toJson (list );
61
76
}
62
77
78
+ /**
79
+ * At http://localhost:23119/libraries/{id}/map <br><br>
80
+ *
81
+ * Looks for the .jmp file in the directory of the given library ({id}.bib file).
82
+ *
83
+ * @param id The given library
84
+ * @return A JSON String containing the mindmap data. If no {id}.jmp file was found, returns the standard mindmap
85
+ * @throws IOException
86
+ */
87
+ @ GET
88
+ @ Path ("map" )
89
+ @ Produces (MediaType .APPLICATION_JSON )
90
+ public String getJabMapJson (@ PathParam ("id" ) String id ) throws IOException {
91
+ boolean isDemo = "demo" .equals (id );
92
+ java .nio .file .Path jabMapPath ;
93
+ if (isDemo ) {
94
+ jabMapPath = getJabMapDemoPath ();
95
+ } else {
96
+ jabMapPath = getJabMapPath (id );
97
+ }
98
+ // if no file is found, return the default mindmap
99
+ if (!Files .exists (jabMapPath )) {
100
+ return """
101
+ {
102
+ "map": {
103
+ "meta": {
104
+ "name": "JabMap",
105
+ "author": "JabMap",
106
+ "version": "1.0"
107
+ },
108
+ "format": "node_tree",
109
+ "data": {
110
+ "id": "root",
111
+ "topic": "JabMap",
112
+ "expanded": true,
113
+ "icons": [],
114
+ "highlight": null,
115
+ "type": "Text"
116
+ }
117
+ }
118
+ }
119
+ """ ;
120
+ }
121
+ return Files .readString (jabMapPath );
122
+ }
123
+
124
+ /**
125
+ * At http://localhost:23119/libraries/{id}/map <br><br>
126
+ *
127
+ * Saves the mindmap next to its associated library.
128
+ *
129
+ * @param id The given library
130
+ *
131
+ * @throws IOException
132
+ */
133
+ @ PUT
134
+ @ Path ("map" )
135
+ @ Consumes (MediaType .APPLICATION_JSON )
136
+ public void updateJabMapJson (@ PathParam ("id" ) String id , String fileContent ) throws IOException {
137
+ boolean isDemo = "demo" .equals (id );
138
+ java .nio .file .Path targetPath ;
139
+ if (isDemo ) {
140
+ targetPath = getJabMapDemoPath ();
141
+ } else {
142
+ targetPath = getJabMapPath (id );
143
+ }
144
+ Files .writeString (targetPath , fileContent );
145
+ }
146
+
63
147
@ GET
64
148
@ Produces (JabrefMediaType .JSON_CSL_ITEM )
65
149
public String getClsItemJson (@ PathParam ("id" ) String id ) throws IOException {
@@ -94,17 +178,165 @@ public Response getBibtex(@PathParam("id") String id) {
94
178
throw new InternalServerErrorException ("Could not read library " + library , e );
95
179
}
96
180
return Response .ok ()
97
- .header ("Content-Disposition" , "attachment; filename=\" " + library .getFileName () + "\" " )
98
- .entity (libraryAsString )
99
- .build ();
181
+ .header ("Content-Disposition" , "attachment; filename=\" " + library .getFileName () + "\" " )
182
+ .entity (libraryAsString )
183
+ .build ();
184
+ }
185
+
186
+ private java .nio .file .Path getJabMapPath (String id ) {
187
+ java .nio .file .Path libraryPath = ServerUtils .getLibraryPath (id , filesToServe , contextsToServe );
188
+ String newName = libraryPath .getFileName ().toString ().replaceFirst ("\\ .bib$" , ".jmp" );
189
+ return libraryPath .getParent ().resolve (newName );
190
+ }
191
+
192
+ private java .nio .file .Path getJabMapDemoPath () {
193
+ java .nio .file .Path result = java .nio .file .Path .of (System .getProperty ("java.io.tmpdir" )).resolve ("demo.jmp" );
194
+ LOGGER .debug ("Using temporary file for demo jmp: {}" , result );
195
+ return result ;
100
196
}
101
197
102
- /// @param id - also "demo" for the Chocolate.bib file
198
+ /**
199
+ * @param id - also "demo" for the Chocolate.bib file
200
+ */
103
201
private BibDatabaseContext getDatabaseContext (String id ) throws IOException {
104
202
return ServerUtils .getBibDatabaseContext (id , filesToServe , contextsToServe , preferences .getImportFormatPreferences ());
105
203
}
106
204
107
- /// @return a stream to the Chocolate.bib file in the classpath (is null only if the file was moved or there are issues with the classpath)
205
+ /**
206
+ * At http://localhost:23119/libraries/{id}/entries/{entryId} <br><br>
207
+ *
208
+ * Combines attributes of a given BibEntry into a basic entry preview for as plain text.
209
+ *
210
+ * @param id The name of the library
211
+ * @param entryId The CitationKey of the BibEntry
212
+ * @return a basic entry preview as plain text
213
+ * @throws IOException
214
+ * @throws NotFoundException
215
+ */
216
+ @ GET
217
+ @ Path ("entries/{entryId}" )
218
+ @ Produces (MediaType .TEXT_PLAIN + ";charset=UTF-8" )
219
+ public String getPlainRepresentation (@ PathParam ("id" ) String id , @ PathParam ("entryId" ) String entryId ) throws IOException {
220
+ BibDatabaseContext databaseContext = getDatabaseContext (id );
221
+ List <BibEntry > entriesByCitationKey = databaseContext .getDatabase ().getEntriesByCitationKey (entryId );
222
+ if (entriesByCitationKey .isEmpty ()) {
223
+ throw new NotFoundException ("Entry with citation key '" + entryId + "' not found in library " + id );
224
+ }
225
+ if (entriesByCitationKey .size () > 1 ) {
226
+ LOGGER .warn ("Multiple entries found with citation key '{}'. Using the first one." , entryId );
227
+ }
228
+
229
+ // TODO: Currently, the preview preferences are in GUI package, which is not accessible here.
230
+ // build the preview
231
+ BibEntry entry = entriesByCitationKey .getFirst ();
232
+
233
+ String author = entry .getField (StandardField .AUTHOR ).orElse ("(N/A)" );
234
+ String title = entry .getField (StandardField .TITLE ).orElse ("(N/A)" );
235
+ String journal = entry .getField (StandardField .JOURNAL ).orElse ("(N/A)" );
236
+ String volume = entry .getField (StandardField .VOLUME ).orElse ("(N/A)" );
237
+ String number = entry .getField (StandardField .NUMBER ).orElse ("(N/A)" );
238
+ String pages = entry .getField (StandardField .PAGES ).orElse ("(N/A)" );
239
+ String releaseDate = entry .getField (StandardField .DATE ).orElse ("(N/A)" );
240
+
241
+ // the only difference to the HTML version of this method is the format of the output:
242
+ String preview =
243
+ "Author: " + author
244
+ + "\n Title: " + title
245
+ + "\n Journal: " + journal
246
+ + "\n Volume: " + volume
247
+ + "\n Number: " + number
248
+ + "\n Pages: " + pages
249
+ + "\n Released on: " + releaseDate ;
250
+
251
+ return preview ;
252
+ }
253
+
254
+ /**
255
+ * At http://localhost:23119/libraries/{id}/entries/{entryId} <br><br>
256
+ *
257
+ * Combines attributes of a given BibEntry into a basic entry preview for as HTML text.
258
+ *
259
+ * @param id The name of the library
260
+ * @param entryId The CitationKey of the BibEntry
261
+ * @return a basic entry preview as HTML text
262
+ * @throws IOException
263
+ */
264
+ @ GET
265
+ @ Path ("entries/{entryId}" )
266
+ @ Produces (MediaType .TEXT_HTML + ";charset=UTF-8" )
267
+ public String getHTMLRepresentation (@ PathParam ("id" ) String id , @ PathParam ("entryId" ) String entryId ) throws IOException {
268
+ List <BibEntry > entriesByCitationKey = getDatabaseContext (id ).getDatabase ().getEntriesByCitationKey (entryId );
269
+ if (entriesByCitationKey .isEmpty ()) {
270
+ throw new NotFoundException ("Entry with citation key '" + entryId + "' not found in library " + id );
271
+ }
272
+ if (entriesByCitationKey .size () > 1 ) {
273
+ LOGGER .warn ("Multiple entries found with citation key '{}'. Using the first one." , entryId );
274
+ }
275
+
276
+ // TODO: Currently, the preview preferences are in GUI package, which is not accessible here.
277
+ // build the preview
278
+ BibEntry entry = entriesByCitationKey .getFirst ();
279
+
280
+ String author = entry .getField (StandardField .AUTHOR ).orElse ("(N/A)" );
281
+ String title = entry .getField (StandardField .TITLE ).orElse ("(N/A)" );
282
+ String journal = entry .getField (StandardField .JOURNAL ).orElse ("(N/A)" );
283
+ String volume = entry .getField (StandardField .VOLUME ).orElse ("(N/A)" );
284
+ String number = entry .getField (StandardField .NUMBER ).orElse ("(N/A)" );
285
+ String pages = entry .getField (StandardField .PAGES ).orElse ("(N/A)" );
286
+ String releaseDate = entry .getField (StandardField .DATE ).orElse ("(N/A)" );
287
+
288
+ // the only difference to the plain text version of this method is the format of the output:
289
+ String preview =
290
+ "<strong>Author:</strong> " + author + "<br>" +
291
+ "<strong>Title:</strong> " + title + "<br>" +
292
+ "<strong>Journal:</strong> " + journal + "<br>" +
293
+ "<strong>Volume:</strong> " + volume + "<br>" +
294
+ "<strong>Number:</strong> " + number + "<br>" +
295
+ "<strong>Pages:</strong> " + pages + "<br>" +
296
+ "<strong>Released on:</strong> " + releaseDate ;
297
+
298
+ return preview ;
299
+ }
300
+
301
+ /**
302
+ * At http://localhost:23119/libraries/{id}/entries/pdffiles <br><br>
303
+ *
304
+ * Loops through all entries in the specified library and adds attached files of type "PDF" to
305
+ * a list and JSON serialises it.
306
+ */
307
+ @ GET
308
+ @ Path ("entries/pdffiles" )
309
+ @ Produces (MediaType .APPLICATION_JSON + ";charset=UTF-8" )
310
+ public String getPDFFilesAsList (@ PathParam ("id" ) String id ) throws IOException {
311
+ // get a list of all entries in library (specified by "id")
312
+ BibDatabaseContext databaseContext = getDatabaseContext (id );
313
+ List <LinkedPdfFileDTO > response = new ArrayList <>();
314
+ List <BibEntry > entries = databaseContext .getDatabase ().getEntries ();
315
+ if (entries .isEmpty ()) {
316
+ throw new NotFoundException ("No entries found for library: " + id );
317
+ }
318
+
319
+ // loop through all entries to extract pdfs and paths
320
+ for (BibEntry entry : entries ) {
321
+ List <LinkedFile > pathsToFiles = entry .getFiles ();
322
+ if (!pathsToFiles .isEmpty ()) {
323
+ for (LinkedFile file : pathsToFiles ) {
324
+ // ignore all non pdf files and online references
325
+ if (!"PDF" .equals (file .getFileType ()) || LinkedFile .isOnlineLink (file .getLink ())) {
326
+ continue ;
327
+ }
328
+ // add file to response body
329
+ LinkedPdfFileDTO localPdfFile = new LinkedPdfFileDTO (entry , file );
330
+ response .add (localPdfFile );
331
+ }
332
+ }
333
+ }
334
+ return gson .toJson (response );
335
+ }
336
+
337
+ /**
338
+ * @return a stream to the Chocolate.bib file in the classpath (is null only if the file was moved or there are issues with the classpath)
339
+ */
108
340
private @ Nullable InputStream getChocolateBibAsStream () {
109
341
return BibDatabase .class .getResourceAsStream ("/Chocolate.bib" );
110
342
}
0 commit comments