1
1
package org .jabref .logic .exporter ;
2
2
3
3
import java .io .IOException ;
4
+ import java .io .Writer ;
4
5
import java .nio .charset .Charset ;
5
6
import java .nio .charset .StandardCharsets ;
6
7
import java .util .ArrayList ;
16
17
import java .util .regex .Matcher ;
17
18
import java .util .regex .Pattern ;
18
19
20
+ import org .jabref .logic .bibtex .BibEntryWriter ;
19
21
import org .jabref .logic .bibtex .FieldPreferences ;
22
+ import org .jabref .logic .bibtex .FieldWriter ;
23
+ import org .jabref .logic .bibtex .InvalidFieldValueException ;
20
24
import org .jabref .logic .bibtex .comparator .BibtexStringComparator ;
21
25
import org .jabref .logic .bibtex .comparator .CrossRefEntryComparator ;
22
26
import org .jabref .logic .bibtex .comparator .FieldComparator ;
44
48
import org .jabref .model .strings .StringUtil ;
45
49
46
50
import org .jooq .lambda .Unchecked ;
51
+ import org .slf4j .Logger ;
52
+ import org .slf4j .LoggerFactory ;
47
53
48
54
/**
49
- * A generic writer for our database. This is independent of the concrete serialization format.
50
- * For instance, we could also write out YAML or XML by subclassing this class.
55
+ * Writes a .bib file following the BibTeX / BibLaTeX format using the provided {@link BibWriter}
51
56
* <p>
52
- * Currently, {@link BibtexDatabaseWriter} is the only subclass of this class (and that class writes a .bib file)
53
- * <p>
54
- * The opposite class is {@link org.jabref.logic.importer.fileformat.BibtexParser}
57
+ * The opposite class is {@link org.jabref.logic.importer.fileformat.BibtexImporter}
55
58
*/
56
- public abstract class BibDatabaseWriter {
57
-
59
+ public class BibDatabaseWriter {
58
60
public enum SaveType { WITH_JABREF_META_DATA , PLAIN_BIBTEX }
59
61
62
+ public static final String DATABASE_ID_PREFIX = "DBID:" ;
63
+ private static final Logger LOGGER = LoggerFactory .getLogger (BibDatabaseWriter .class );
60
64
private static final Pattern REFERENCE_PATTERN = Pattern .compile ("(#[A-Za-z]+#)" ); // Used to detect string references in strings
65
+ private static final String COMMENT_PREFIX = "@Comment" ;
66
+ private static final String PREAMBLE_PREFIX = "@Preamble" ;
67
+
68
+ private static final String STRING_PREFIX = "@String" ;
69
+
61
70
protected final BibWriter bibWriter ;
62
71
protected final SelfContainedSaveConfiguration saveConfiguration ;
63
72
protected final CitationKeyPatternPreferences keyPatternPreferences ;
@@ -78,6 +87,19 @@ public BibDatabaseWriter(BibWriter bibWriter,
78
87
assert saveConfiguration .getSaveOrder ().getOrderType () != SaveOrder .OrderType .TABLE ;
79
88
}
80
89
90
+ public BibDatabaseWriter (Writer writer ,
91
+ String newline ,
92
+ SelfContainedSaveConfiguration saveConfiguration ,
93
+ FieldPreferences fieldPreferences ,
94
+ CitationKeyPatternPreferences citationKeyPatternPreferences ,
95
+ BibEntryTypesManager entryTypesManager ) {
96
+ this (new BibWriter (writer , newline ),
97
+ saveConfiguration ,
98
+ fieldPreferences ,
99
+ citationKeyPatternPreferences ,
100
+ entryTypesManager );
101
+ }
102
+
81
103
private static List <FieldChange > applySaveActions (List <BibEntry > toChange , MetaData metaData , FieldPreferences fieldPreferences ) {
82
104
List <FieldChange > changes = new ArrayList <>();
83
105
@@ -224,11 +246,31 @@ public void savePartOfDatabase(BibDatabaseContext bibDatabaseContext, List<BibEn
224
246
writeEpilogue (bibDatabaseContext .getDatabase ().getEpilog ());
225
247
}
226
248
227
- protected abstract void writeProlog (BibDatabaseContext bibDatabaseContext , Charset encoding ) throws IOException ;
249
+ protected void writeProlog (BibDatabaseContext bibDatabaseContext , Charset encoding ) throws IOException {
250
+ // We write the encoding if
251
+ // - it is provided (!= null)
252
+ // - explicitly set in the .bib file OR not equal to UTF_8
253
+ // Otherwise, we do not write anything and return
254
+ if ((encoding == null ) || (!bibDatabaseContext .getMetaData ().getEncodingExplicitlySupplied () && (encoding .equals (StandardCharsets .UTF_8 )))) {
255
+ return ;
256
+ }
257
+
258
+ // Writes the file encoding information.
259
+ bibWriter .write ("% " );
260
+ bibWriter .writeLine (SaveConfiguration .ENCODING_PREFIX + encoding );
261
+ }
228
262
229
- protected abstract void writeEntry (BibEntry entry , BibDatabaseMode mode ) throws IOException ;
263
+ protected void writeEntry (BibEntry entry , BibDatabaseMode mode ) throws IOException {
264
+ BibEntryWriter bibtexEntryWriter = new BibEntryWriter (new FieldWriter (fieldPreferences ), entryTypesManager );
265
+ bibtexEntryWriter .write (entry , bibWriter , mode , saveConfiguration .shouldReformatFile ());
266
+ }
230
267
231
- protected abstract void writeEpilogue (String epilogue ) throws IOException ;
268
+ protected void writeEpilogue (String epilogue ) throws IOException {
269
+ if (!StringUtil .isNullOrEmpty (epilogue )) {
270
+ bibWriter .write (epilogue );
271
+ bibWriter .finishBlock ();
272
+ }
273
+ }
232
274
233
275
/**
234
276
* Writes all data to the specified writer, using each object's toString() method.
@@ -244,11 +286,31 @@ protected void writeMetaData(MetaData metaData, GlobalCitationKeyPatterns global
244
286
}
245
287
}
246
288
247
- protected abstract void writeMetaDataItem (Map .Entry <String , String > metaItem ) throws IOException ;
289
+ protected void writeMetaDataItem (Map .Entry <String , String > metaItem ) throws IOException {
290
+ bibWriter .write (COMMENT_PREFIX + "{" );
291
+ bibWriter .write (MetaData .META_FLAG );
292
+ bibWriter .write (metaItem .getKey ());
293
+ bibWriter .write (":" );
294
+ bibWriter .write (metaItem .getValue ());
295
+ bibWriter .write ("}" );
296
+ bibWriter .finishBlock ();
297
+ }
248
298
249
- protected abstract void writePreamble (String preamble ) throws IOException ;
299
+ protected void writePreamble (String preamble ) throws IOException {
300
+ if (!StringUtil .isNullOrEmpty (preamble )) {
301
+ bibWriter .write (PREAMBLE_PREFIX + "{" );
302
+ bibWriter .write (preamble );
303
+ bibWriter .writeLine ("}" );
304
+ bibWriter .finishBlock ();
305
+ }
306
+ }
250
307
251
- protected abstract void writeDatabaseID (String sharedDatabaseID ) throws IOException ;
308
+ protected void writeDatabaseID (String sharedDatabaseID ) throws IOException {
309
+ bibWriter .write ("% " );
310
+ bibWriter .write (DATABASE_ID_PREFIX );
311
+ bibWriter .write (" " );
312
+ bibWriter .writeLine (sharedDatabaseID );
313
+ }
252
314
253
315
/**
254
316
* Write all strings in alphabetical order, modified to produce a safe (for BibTeX) order of the strings if they
@@ -307,16 +369,49 @@ protected void writeString(BibtexString bibtexString, Map<String, BibtexString>
307
369
writeString (bibtexString , maxKeyLength );
308
370
}
309
371
310
- protected abstract void writeString (BibtexString bibtexString , int maxKeyLength )
311
- throws IOException ;
372
+ protected void writeString (BibtexString bibtexString , int maxKeyLength ) throws IOException {
373
+ // If the string has not been modified, write it back as it was
374
+ if (!saveConfiguration .shouldReformatFile () && !bibtexString .hasChanged ()) {
375
+ LOGGER .debug ("Writing parsed serialization {}." , bibtexString .getParsedSerialization ());
376
+ bibWriter .write (bibtexString .getParsedSerialization ());
377
+ return ;
378
+ }
379
+
380
+ // Write user comments
381
+ String userComments = bibtexString .getUserComments ();
382
+ if (!userComments .isEmpty ()) {
383
+ bibWriter .writeLine (userComments );
384
+ }
385
+
386
+ bibWriter .write (STRING_PREFIX + "{" + bibtexString .getName () + StringUtil
387
+ .repeatSpaces (maxKeyLength - bibtexString .getName ().length ()) + " = " );
388
+ if (bibtexString .getContent ().isEmpty ()) {
389
+ bibWriter .write ("{}" );
390
+ } else {
391
+ try {
392
+ String formatted = new FieldWriter (fieldPreferences )
393
+ .write (InternalField .BIBTEX_STRING , bibtexString .getContent ());
394
+ bibWriter .write (formatted );
395
+ } catch (InvalidFieldValueException ex ) {
396
+ throw new IOException (ex );
397
+ }
398
+ }
399
+
400
+ bibWriter .writeLine ("}" );
401
+ }
312
402
313
403
protected void writeEntryTypeDefinitions (SortedSet <BibEntryType > types ) throws IOException {
314
404
for (BibEntryType type : types ) {
315
405
writeEntryTypeDefinition (type );
316
406
}
317
407
}
318
408
319
- protected abstract void writeEntryTypeDefinition (BibEntryType customType ) throws IOException ;
409
+ protected void writeEntryTypeDefinition (BibEntryType customType ) throws IOException {
410
+ bibWriter .write (COMMENT_PREFIX + "{" );
411
+ bibWriter .write (MetaDataSerializer .serializeCustomEntryTypes (customType ));
412
+ bibWriter .writeLine ("}" );
413
+ bibWriter .finishBlock ();
414
+ }
320
415
321
416
/**
322
417
* Generate keys for all entries that are lacking keys.
0 commit comments