value = entry.getField(field);
- // only write field if is is not empty
+ // only write field if it is not empty
// field.ifPresent does not work as an IOException may be thrown
if (value.isPresent() && !value.get().trim().isEmpty()) {
out.write(" " + getFormattedFieldName(field, indentation));
@@ -170,7 +162,7 @@ private void writeField(BibEntry entry, Writer out, Field field, int indentation
} catch (InvalidFieldValueException ex) {
throw new IOException("Error in field '" + field + " of entry " + entry.getCitationKey().orElse("") + "': " + ex.getMessage(), ex);
}
- out.write(',' + OS.NEWLINE);
+ out.writeLine(",");
}
}
@@ -187,19 +179,17 @@ private int getLengthOfLongestFieldName(BibEntry entry) {
/**
* Get display version of a entry field.
*
- * BibTeX is case-insensitive therefore there is no difference between:
- * howpublished, HOWPUBLISHED, HowPublished, etc.
+ * BibTeX is case-insensitive therefore there is no difference between: howpublished, HOWPUBLISHED, HowPublished, etc.
*
- * The was a long discussion about how JabRef should write the fields.
- * See https://github.com/JabRef/jabref/issues/116
+ * There was a long discussion about how JabRef should write the fields. See https://github.com/JabRef/jabref/issues/116
*
* The team decided to do the biblatex way and use lower case for the field names.
*
* @param field The name of the field.
* @return The display version of the field name.
*/
- private String getFormattedFieldName(Field field, int intendation) {
+ private String getFormattedFieldName(Field field, int intention) {
String fieldName = field.getName();
- return fieldName.toLowerCase(Locale.ROOT) + StringUtil.repeatSpaces(intendation - fieldName.length()) + " = ";
+ return fieldName.toLowerCase(Locale.ROOT) + StringUtil.repeatSpaces(intention - fieldName.length()) + " = ";
}
}
diff --git a/src/main/java/org/jabref/logic/bibtex/comparator/EntryComparator.java b/src/main/java/org/jabref/logic/bibtex/comparator/EntryComparator.java
index 871a1516269..3892d220251 100644
--- a/src/main/java/org/jabref/logic/bibtex/comparator/EntryComparator.java
+++ b/src/main/java/org/jabref/logic/bibtex/comparator/EntryComparator.java
@@ -24,6 +24,13 @@ public class EntryComparator implements Comparator {
private final boolean binary;
private final Comparator next;
+ /**
+ *
+ * @param binary true: the presence of fields is checked; false: the content of the fields is compared
+ * @param descending true: if the most different entry should get the highest score
+ * @param field the field to sort on
+ * @param next the next comparator to use (if the current comparator results in equality)
+ */
public EntryComparator(boolean binary, boolean descending, Field field, Comparator next) {
this.binary = binary;
this.sortField = field;
@@ -42,7 +49,7 @@ public EntryComparator(boolean binary, boolean descending, Field field) {
public int compare(BibEntry e1, BibEntry e2) {
// default equals
// TODO: with the new default equals this does not only return 0 for identical objects,
- // but for all objects that have the same id and same fields
+ // but for all objects that have the same id and same fields
if (Objects.equals(e1, e2)) {
return 0;
}
@@ -103,21 +110,21 @@ public int compare(BibEntry e1, BibEntry e2) {
int result;
if ((f1 instanceof Integer) && (f2 instanceof Integer)) {
- result = -((Integer) f1).compareTo((Integer) f2);
+ result = ((Integer) f1).compareTo((Integer) f2);
} else if (f2 instanceof Integer) {
Integer f1AsInteger = Integer.valueOf(f1.toString());
- result = -f1AsInteger.compareTo((Integer) f2);
+ result = f1AsInteger.compareTo((Integer) f2);
} else if (f1 instanceof Integer) {
Integer f2AsInteger = Integer.valueOf(f2.toString());
- result = -((Integer) f1).compareTo(f2AsInteger);
+ result = ((Integer) f1).compareTo(f2AsInteger);
} else {
String ours = ((String) f1).toLowerCase(Locale.ROOT);
String theirs = ((String) f2).toLowerCase(Locale.ROOT);
int comp = ours.compareTo(theirs);
- result = -comp;
+ result = comp;
}
if (result != 0) {
- return descending ? result : -result; // Primary sort.
+ return descending ? -result : result; // Primary sort.
}
if (next == null) {
return idCompare(e1, e2); // If still equal, we use the unique IDs.
diff --git a/src/main/java/org/jabref/logic/cleanup/FieldFormatterCleanups.java b/src/main/java/org/jabref/logic/cleanup/FieldFormatterCleanups.java
index d480f5bfc9f..41e49fa0422 100644
--- a/src/main/java/org/jabref/logic/cleanup/FieldFormatterCleanups.java
+++ b/src/main/java/org/jabref/logic/cleanup/FieldFormatterCleanups.java
@@ -27,7 +27,7 @@ public FieldFormatterCleanups(boolean enabled, List actio
this.actions = Objects.requireNonNull(actions);
}
- private static String getMetaDataString(List actionList, String newline) {
+ private static String getMetaDataString(List actionList, String newLineSeparator) {
// first, group all formatters by the field for which they apply
Map> groupedByField = new TreeMap<>(Comparator.comparing(Field::getName));
for (FieldFormatterCleanup cleanup : actionList) {
@@ -50,7 +50,7 @@ private static String getMetaDataString(List actionList,
for (Map.Entry> entry : groupedByField.entrySet()) {
result.append(entry.getKey().getName());
- StringJoiner joiner = new StringJoiner(",", "[", "]" + newline);
+ StringJoiner joiner = new StringJoiner(",", "[", "]" + newLineSeparator);
entry.getValue().forEach(joiner::add);
result.append(joiner.toString());
}
diff --git a/src/main/java/org/jabref/logic/crawler/Crawler.java b/src/main/java/org/jabref/logic/crawler/Crawler.java
index b54f371f4f2..8df3e5d4154 100644
--- a/src/main/java/org/jabref/logic/crawler/Crawler.java
+++ b/src/main/java/org/jabref/logic/crawler/Crawler.java
@@ -9,10 +9,10 @@
import org.jabref.logic.git.SlrGitHandler;
import org.jabref.logic.importer.ImportFormatPreferences;
import org.jabref.logic.importer.ParseException;
-import org.jabref.logic.preferences.TimestampPreferences;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.study.QueryResult;
import org.jabref.model.util.FileUpdateMonitor;
+import org.jabref.preferences.GeneralPreferences;
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -32,8 +32,8 @@ public class Crawler {
*
* @param studyRepositoryRoot The path to the study repository
*/
- public Crawler(Path studyRepositoryRoot, SlrGitHandler gitHandler, ImportFormatPreferences importFormatPreferences, SavePreferences savePreferences, TimestampPreferences timestampPreferences, BibEntryTypesManager bibEntryTypesManager, FileUpdateMonitor fileUpdateMonitor) throws IllegalArgumentException, IOException, ParseException {
- studyRepository = new StudyRepository(studyRepositoryRoot, gitHandler, importFormatPreferences, fileUpdateMonitor, savePreferences, bibEntryTypesManager);
+ public Crawler(Path studyRepositoryRoot, SlrGitHandler gitHandler, GeneralPreferences generalPreferences, ImportFormatPreferences importFormatPreferences, SavePreferences savePreferences, BibEntryTypesManager bibEntryTypesManager, FileUpdateMonitor fileUpdateMonitor) throws IllegalArgumentException, IOException, ParseException {
+ studyRepository = new StudyRepository(studyRepositoryRoot, gitHandler, generalPreferences, importFormatPreferences, fileUpdateMonitor, savePreferences, bibEntryTypesManager);
StudyDatabaseToFetcherConverter studyDatabaseToFetcherConverter = new StudyDatabaseToFetcherConverter(studyRepository.getActiveLibraryEntries(), importFormatPreferences);
this.studyFetcher = new StudyFetcher(studyDatabaseToFetcherConverter.getActiveFetchers(), studyRepository.getSearchQueryStrings());
}
diff --git a/src/main/java/org/jabref/logic/crawler/StudyRepository.java b/src/main/java/org/jabref/logic/crawler/StudyRepository.java
index cab44b804da..e876d94f0fa 100644
--- a/src/main/java/org/jabref/logic/crawler/StudyRepository.java
+++ b/src/main/java/org/jabref/logic/crawler/StudyRepository.java
@@ -15,7 +15,7 @@
import org.jabref.logic.citationkeypattern.CitationKeyGenerator;
import org.jabref.logic.database.DatabaseMerger;
-import org.jabref.logic.exporter.AtomicFileWriter;
+import org.jabref.logic.exporter.BibWriter;
import org.jabref.logic.exporter.BibtexDatabaseWriter;
import org.jabref.logic.exporter.SaveException;
import org.jabref.logic.exporter.SavePreferences;
@@ -25,6 +25,7 @@
import org.jabref.logic.importer.ParseException;
import org.jabref.logic.importer.SearchBasedFetcher;
import org.jabref.logic.l10n.Localization;
+import org.jabref.logic.util.OS;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntryTypesManager;
@@ -34,6 +35,7 @@
import org.jabref.model.study.StudyDatabase;
import org.jabref.model.study.StudyQuery;
import org.jabref.model.util.FileUpdateMonitor;
+import org.jabref.preferences.GeneralPreferences;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.slf4j.Logger;
@@ -61,6 +63,7 @@ class StudyRepository {
private final Path studyDefinitionFile;
private final SlrGitHandler gitHandler;
private final Study study;
+ private final GeneralPreferences generalPreferences;
private final ImportFormatPreferences importFormatPreferences;
private final FileUpdateMonitor fileUpdateMonitor;
private final SavePreferences savePreferences;
@@ -79,12 +82,14 @@ class StudyRepository {
*/
public StudyRepository(Path pathToRepository,
SlrGitHandler gitHandler,
+ GeneralPreferences generalPreferences,
ImportFormatPreferences importFormatPreferences,
FileUpdateMonitor fileUpdateMonitor,
SavePreferences savePreferences,
BibEntryTypesManager bibEntryTypesManager) throws IOException, ParseException {
this.repositoryPath = pathToRepository;
this.gitHandler = gitHandler;
+ this.generalPreferences = generalPreferences;
this.importFormatPreferences = importFormatPreferences;
this.fileUpdateMonitor = fileUpdateMonitor;
this.studyDefinitionFile = Path.of(repositoryPath.toString(), STUDY_DEFINITION_FILE_NAME);
@@ -132,7 +137,7 @@ public StudyRepository(Path pathToRepository,
*/
public BibDatabaseContext getFetcherResultEntries(String query, String fetcherName) throws IOException {
if (Files.exists(getPathToFetcherResultFile(query, fetcherName))) {
- return OpenDatabase.loadDatabase(getPathToFetcherResultFile(query, fetcherName), importFormatPreferences, fileUpdateMonitor).getDatabaseContext();
+ return OpenDatabase.loadDatabase(getPathToFetcherResultFile(query, fetcherName), generalPreferences, importFormatPreferences, fileUpdateMonitor).getDatabaseContext();
}
return new BibDatabaseContext();
}
@@ -142,7 +147,7 @@ public BibDatabaseContext getFetcherResultEntries(String query, String fetcherNa
*/
public BibDatabaseContext getQueryResultEntries(String query) throws IOException {
if (Files.exists(getPathToQueryResultFile(query))) {
- return OpenDatabase.loadDatabase(getPathToQueryResultFile(query), importFormatPreferences, fileUpdateMonitor).getDatabaseContext();
+ return OpenDatabase.loadDatabase(getPathToQueryResultFile(query), generalPreferences, importFormatPreferences, fileUpdateMonitor).getDatabaseContext();
}
return new BibDatabaseContext();
}
@@ -152,7 +157,7 @@ public BibDatabaseContext getQueryResultEntries(String query) throws IOException
*/
public BibDatabaseContext getStudyResultEntries() throws IOException {
if (Files.exists(getPathToStudyResultFile())) {
- return OpenDatabase.loadDatabase(getPathToStudyResultFile(), importFormatPreferences, fileUpdateMonitor).getDatabaseContext();
+ return OpenDatabase.loadDatabase(getPathToStudyResultFile(), generalPreferences, importFormatPreferences, fileUpdateMonitor).getDatabaseContext();
}
return new BibDatabaseContext();
}
@@ -412,14 +417,11 @@ private void writeResultToFile(Path pathToFile, BibDatabase entries) throws IOEx
Files.createFile(pathToFile);
}
try (Writer fileWriter = new FileWriter(pathToFile.toFile())) {
- BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter(fileWriter, savePreferences, bibEntryTypesManager);
- databaseWriter.saveDatabase(new BibDatabaseContext(entries));
- }
- try (AtomicFileWriter fileWriter = new AtomicFileWriter(pathToFile, savePreferences.getEncoding(), savePreferences.shouldMakeBackup())) {
- BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter(fileWriter, savePreferences, bibEntryTypesManager);
+ BibWriter bibWriter = new BibWriter(fileWriter, OS.NEWLINE);
+ BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter(bibWriter, generalPreferences, savePreferences, bibEntryTypesManager);
databaseWriter.saveDatabase(new BibDatabaseContext(entries));
} catch (UnsupportedCharsetException ex) {
- throw new SaveException(Localization.lang("Character encoding '%0' is not supported.", savePreferences.getEncoding().displayName()), ex);
+ throw new SaveException(Localization.lang("Character encoding '%0' is not supported.", generalPreferences.getDefaultEncoding().displayName()), ex);
} catch (IOException ex) {
throw new SaveException("Problems saving: " + ex, ex);
}
diff --git a/src/main/java/org/jabref/logic/database/DuplicateCheck.java b/src/main/java/org/jabref/logic/database/DuplicateCheck.java
index f7de07e79cf..fbcd0dd4c3f 100644
--- a/src/main/java/org/jabref/logic/database/DuplicateCheck.java
+++ b/src/main/java/org/jabref/logic/database/DuplicateCheck.java
@@ -10,6 +10,7 @@
import java.util.Set;
import java.util.stream.Collectors;
+import org.jabref.logic.util.OS;
import org.jabref.logic.util.strings.StringSimilarity;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseMode;
@@ -25,6 +26,7 @@
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.identifier.DOI;
import org.jabref.model.entry.identifier.ISBN;
+import org.jabref.model.strings.StringUtil;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
@@ -101,13 +103,16 @@ private static boolean haveDifferentChaptersOrPagesOfTheSameBook(final BibEntry
private static double[] compareRequiredFields(final BibEntryType type, final BibEntry one, final BibEntry two) {
final Set requiredFields = type.getRequiredFields();
- return requiredFields == null
+ return requiredFields.isEmpty()
? new double[] {0., 0.}
: DuplicateCheck.compareFieldSet(requiredFields.stream().map(OrFields::getPrimary).collect(Collectors.toSet()), one, two);
}
private static boolean isFarFromThreshold(double value) {
- return Math.abs(value - DuplicateCheck.DUPLICATE_THRESHOLD) > DuplicateCheck.DOUBT_RANGE;
+ if (value < 0.0) {
+ LOGGER.debug("Value {} is below zero. Should not happen", value);
+ }
+ return value - DuplicateCheck.DUPLICATE_THRESHOLD > DuplicateCheck.DOUBT_RANGE;
}
private static boolean compareOptionalFields(final BibEntryType type,
@@ -115,7 +120,7 @@ private static boolean compareOptionalFields(final BibEntryType type,
final BibEntry two,
final double[] req) {
final Set optionalFields = type.getOptionalFields();
- if (optionalFields == null) {
+ if (optionalFields.isEmpty()) {
return req[0] >= DuplicateCheck.DUPLICATE_THRESHOLD;
}
final double[] opt = DuplicateCheck.compareFieldSet(optionalFields.stream().map(BibField::getField).collect(Collectors.toSet()), one, two);
@@ -126,22 +131,26 @@ private static boolean compareOptionalFields(final BibEntryType type,
}
private static double[] compareFieldSet(final Collection fields, final BibEntry one, final BibEntry two) {
- double res = 0;
- double totWeights = 0.;
+ if (fields.isEmpty()) {
+ return new double[] {0.0, 0.0};
+ }
+ double equalWeights = 0;
+ double totalWeights = 0.;
for (final Field field : fields) {
- final double weight = DuplicateCheck.FIELD_WEIGHTS.getOrDefault(field, 1.0);
- totWeights += weight;
+ final double currentWeight = DuplicateCheck.FIELD_WEIGHTS.getOrDefault(field, 1.0);
+ totalWeights += currentWeight;
int result = DuplicateCheck.compareSingleField(field, one, two);
if (result == EQUAL) {
- res += weight;
+ equalWeights += currentWeight;
} else if (result == EMPTY_IN_BOTH) {
- totWeights -= weight;
+ totalWeights -= currentWeight;
}
}
- if (totWeights > 0) {
- return new double[] {res / totWeights, totWeights};
+ if (totalWeights > 0) {
+ return new double[] {equalWeights / totalWeights, totalWeights};
}
- return new double[] {0.5, 0.0};
+ // all fields are empty in both --> have no difference at all
+ return new double[] {0.0, 0.0};
}
private static int compareSingleField(final Field field, final BibEntry one, final BibEntry two) {
@@ -220,8 +229,8 @@ private static int compareChapterField(final String stringOne, final String stri
}
private static int compareField(final String stringOne, final String stringTwo) {
- final String processedStringOne = stringOne.toLowerCase(Locale.ROOT).trim();
- final String processedStringTwo = stringTwo.toLowerCase(Locale.ROOT).trim();
+ final String processedStringOne = StringUtil.unifyLineBreaks(stringOne.toLowerCase(Locale.ROOT).trim(), OS.NEWLINE);
+ final String processedStringTwo = StringUtil.unifyLineBreaks(stringTwo.toLowerCase(Locale.ROOT).trim(), OS.NEWLINE);
final double similarity = DuplicateCheck.correlateByWords(processedStringOne, processedStringTwo);
if (similarity > 0.8) {
return EQUAL;
@@ -236,9 +245,7 @@ public static double compareEntriesStrictly(BibEntry one, BibEntry two) {
int score = 0;
for (final Field field : allFields) {
- final Optional stringOne = one.getField(field);
- final Optional stringTwo = two.getField(field);
- if (stringOne.equals(stringTwo)) {
+ if (isSingleFieldEqual(one, two, field)) {
score++;
}
}
@@ -248,6 +255,19 @@ public static double compareEntriesStrictly(BibEntry one, BibEntry two) {
return (double) score / allFields.size();
}
+ private static boolean isSingleFieldEqual(BibEntry one, BibEntry two, Field field) {
+ final Optional stringOne = one.getField(field);
+ final Optional stringTwo = two.getField(field);
+ if (stringOne.isEmpty() && stringTwo.isEmpty()) {
+ return true;
+ }
+ if (stringOne.isEmpty() || stringTwo.isEmpty()) {
+ return false;
+ }
+ return (StringUtil.unifyLineBreaks(stringOne.get(), OS.NEWLINE).equals(
+ StringUtil.unifyLineBreaks(stringTwo.get(), OS.NEWLINE)));
+ }
+
/**
* Compare two strings on the basis of word-by-word correlation analysis.
*
@@ -293,7 +313,7 @@ private static double similarity(final String first, final String second) {
}
final double distanceIgnoredCase = new StringSimilarity().editDistanceIgnoreCase(longer, shorter);
final double similarity = (longerLength - distanceIgnoredCase) / longerLength;
- LOGGER.debug("Longer string: " + longer + " Shorter string: " + shorter + " Similarity: " + similarity);
+ LOGGER.debug("Longer string: {} Shorter string: {} Similarity: {}", longer, shorter, similarity);
return similarity;
}
@@ -330,7 +350,8 @@ public boolean isDuplicate(final BibEntry one, final BibEntry two, final BibData
final Optional type = entryTypesManager.enrich(one.getType(), bibDatabaseMode);
if (type.isPresent()) {
- final double[] reqCmpResult = compareRequiredFields(type.get(), one, two);
+ BibEntryType entryType = type.get();
+ final double[] reqCmpResult = compareRequiredFields(entryType, one, two);
if (isFarFromThreshold(reqCmpResult[0])) {
// Far from the threshold value, so we base our decision on the required fields only
@@ -338,11 +359,13 @@ public boolean isDuplicate(final BibEntry one, final BibEntry two, final BibData
}
// Close to the threshold value, so we take a look at the optional fields, if any:
- return compareOptionalFields(type.get(), one, two, reqCmpResult);
- } else {
- // We don't know about the type, so simply compare fields without any distinction between optional/required
- return compareFieldSet(Sets.union(one.getFields(), two.getFields()), one, two)[0] >= DuplicateCheck.DUPLICATE_THRESHOLD;
+ if (compareOptionalFields(type.get(), one, two, reqCmpResult)) {
+ return true;
+ }
}
+ // if type is not present, so simply compare fields without any distinction between optional/required
+ // In case both required and optional fields are equal, we also use this fallback
+ return compareFieldSet(Sets.union(one.getFields(), two.getFields()), one, two)[0] >= DuplicateCheck.DUPLICATE_THRESHOLD;
}
/**
diff --git a/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java b/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java
index d00628caad6..f62215b43ae 100644
--- a/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java
+++ b/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java
@@ -1,7 +1,6 @@
package org.jabref.logic.exporter;
import java.io.IOException;
-import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
@@ -42,18 +41,21 @@
import org.jabref.model.metadata.MetaData;
import org.jabref.model.metadata.SaveOrderConfig;
import org.jabref.model.strings.StringUtil;
+import org.jabref.preferences.GeneralPreferences;
public abstract class BibDatabaseWriter {
private static final Pattern REFERENCE_PATTERN = Pattern.compile("(#[A-Za-z]+#)"); // Used to detect string references in strings
- protected final Writer writer;
- protected final SavePreferences preferences;
+ protected final BibWriter bibWriter;
+ protected final GeneralPreferences generalPreferences;
+ protected final SavePreferences savePreferences;
protected final List saveActionsFieldChanges = new ArrayList<>();
protected final BibEntryTypesManager entryTypesManager;
- public BibDatabaseWriter(Writer writer, SavePreferences preferences, BibEntryTypesManager entryTypesManager) {
- this.writer = Objects.requireNonNull(writer);
- this.preferences = preferences;
+ public BibDatabaseWriter(BibWriter bibWriter, GeneralPreferences generalPreferences, SavePreferences savePreferences, BibEntryTypesManager entryTypesManager) {
+ this.bibWriter = Objects.requireNonNull(bibWriter);
+ this.generalPreferences = generalPreferences;
+ this.savePreferences = savePreferences;
this.entryTypesManager = entryTypesManager;
}
@@ -171,14 +173,13 @@ public void savePartOfDatabase(BibDatabaseContext bibDatabaseContext, List typesToWrite = new TreeSet<>();
-
// Some file formats write something at the start of the file (like the encoding)
- if (preferences.getSaveType() != SavePreferences.DatabaseSaveType.PLAIN_BIBTEX) {
- writePrelogue(bibDatabaseContext, preferences.getEncoding());
+ if (savePreferences.getSaveType() != SavePreferences.DatabaseSaveType.PLAIN_BIBTEX) {
+ writeProlog(bibDatabaseContext, generalPreferences.getDefaultEncoding());
}
+ bibWriter.finishBlock();
+
// Write preamble if there is one.
writePreamble(bibDatabaseContext.getDatabase().getPreamble().orElse(""));
@@ -186,14 +187,17 @@ public void savePartOfDatabase(BibDatabaseContext bibDatabaseContext, List sortedEntries = getSortedEntries(bibDatabaseContext, entries, preferences);
+ List sortedEntries = getSortedEntries(bibDatabaseContext, entries, savePreferences);
List saveActionChanges = applySaveActions(sortedEntries, bibDatabaseContext.getMetaData());
saveActionsFieldChanges.addAll(saveActionChanges);
- if (preferences.getCitationKeyPatternPreferences().shouldGenerateCiteKeysBeforeSaving()) {
+ if (savePreferences.getCitationKeyPatternPreferences().shouldGenerateCiteKeysBeforeSaving()) {
List keyChanges = generateCitationKeys(bibDatabaseContext, sortedEntries);
saveActionsFieldChanges.addAll(keyChanges);
}
+ // Map to collect entry type definitions that we must save along with entries using them.
+ Set typesToWrite = new TreeSet<>();
+
for (BibEntry entry : sortedEntries) {
// Check if we must write the type definition for this
// entry, as well. Our criterion is that all non-standard
@@ -207,9 +211,9 @@ public void savePartOfDatabase(BibDatabaseContext bibDatabaseContext, List remaining, int maxKeyLength)
+ protected void writeString(BibtexString bibtexString, Map remaining, int maxKeyLength)
throws IOException {
// First remove this from the "remaining" list so it can't cause problem with circular refs:
remaining.remove(bibtexString.getName());
@@ -297,14 +299,14 @@ protected void writeString(BibtexString bibtexString, boolean isFirstString, Map
// If the label we found exists as a key in the "remaining" Map, we go on and write it now:
if (remaining.containsKey(label)) {
BibtexString referred = remaining.get(label);
- writeString(referred, isFirstString, remaining, maxKeyLength);
+ writeString(referred, remaining, maxKeyLength);
}
}
- writeString(bibtexString, isFirstString, maxKeyLength);
+ writeString(bibtexString, maxKeyLength);
}
- protected abstract void writeString(BibtexString bibtexString, boolean isFirstString, int maxKeyLength)
+ protected abstract void writeString(BibtexString bibtexString, int maxKeyLength)
throws IOException;
protected void writeEntryTypeDefinitions(Set types) throws IOException {
@@ -320,7 +322,7 @@ protected void writeEntryTypeDefinitions(Set types) throws IOExcep
*/
protected List generateCitationKeys(BibDatabaseContext databaseContext, List entries) {
List changes = new ArrayList<>();
- CitationKeyGenerator keyGenerator = new CitationKeyGenerator(databaseContext, preferences.getCitationKeyPatternPreferences());
+ CitationKeyGenerator keyGenerator = new CitationKeyGenerator(databaseContext, savePreferences.getCitationKeyPatternPreferences());
for (BibEntry bes : entries) {
Optional oldKey = bes.getCitationKey();
if (StringUtil.isBlank(oldKey)) {
diff --git a/src/main/java/org/jabref/logic/exporter/BibWriter.java b/src/main/java/org/jabref/logic/exporter/BibWriter.java
new file mode 100644
index 00000000000..fd03941cf74
--- /dev/null
+++ b/src/main/java/org/jabref/logic/exporter/BibWriter.java
@@ -0,0 +1,72 @@
+package org.jabref.logic.exporter;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.jabref.model.strings.StringUtil;
+
+/**
+ * Class to write to a .bib file. Used by {@link BibtexDatabaseWriter}
+ */
+public class BibWriter {
+
+ private final String newLineSeparator;
+ private final Writer writer;
+
+ private boolean precedingNewLineRequired = false;
+ private boolean somethingWasWritten = false;
+ private boolean lastWriteWasNewline = false;
+
+ /**
+ * @param newLineSeparator the string used for a line break
+ */
+ public BibWriter(Writer writer, String newLineSeparator) {
+ this.writer = writer;
+ this.newLineSeparator = newLineSeparator;
+ }
+
+ /**
+ * Writes the given string. The newlines of the given string are converted to the newline set for this clas
+ */
+ public void write(String string) throws IOException {
+ if (precedingNewLineRequired) {
+ writer.write(newLineSeparator);
+ precedingNewLineRequired = false;
+ }
+ string = StringUtil.unifyLineBreaks(string, newLineSeparator);
+ writer.write(string);
+ lastWriteWasNewline = string.endsWith(newLineSeparator);
+ somethingWasWritten = true;
+ }
+
+ /**
+ * Writes the given string and finishes it with a line break
+ */
+ public void writeLine(String string) throws IOException {
+ this.write(string);
+ this.finishLine();
+ }
+
+ /**
+ * Finishes a line
+ */
+ public void finishLine() throws IOException {
+ if (!this.lastWriteWasNewline) {
+ this.write(newLineSeparator);
+ }
+ }
+
+ /**
+ * Finishes a block
+ */
+ public void finishBlock() throws IOException {
+ if (!somethingWasWritten) {
+ return;
+ }
+ if (!lastWriteWasNewline) {
+ this.finishLine();
+ }
+ this.somethingWasWritten = false;
+ this.precedingNewLineRequired = true;
+ }
+}
diff --git a/src/main/java/org/jabref/logic/exporter/BibtexDatabaseWriter.java b/src/main/java/org/jabref/logic/exporter/BibtexDatabaseWriter.java
index 9e455cf6def..84227448fe9 100644
--- a/src/main/java/org/jabref/logic/exporter/BibtexDatabaseWriter.java
+++ b/src/main/java/org/jabref/logic/exporter/BibtexDatabaseWriter.java
@@ -3,12 +3,12 @@
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.Map;
import org.jabref.logic.bibtex.BibEntryWriter;
import org.jabref.logic.bibtex.FieldWriter;
import org.jabref.logic.bibtex.InvalidFieldValueException;
-import org.jabref.logic.util.OS;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntry;
@@ -18,6 +18,7 @@
import org.jabref.model.entry.field.InternalField;
import org.jabref.model.metadata.MetaData;
import org.jabref.model.strings.StringUtil;
+import org.jabref.preferences.GeneralPreferences;
public class BibtexDatabaseWriter extends BibDatabaseWriter {
@@ -26,110 +27,105 @@ public class BibtexDatabaseWriter extends BibDatabaseWriter {
private static final String COMMENT_PREFIX = "@Comment";
private static final String PREAMBLE_PREFIX = "@Preamble";
- public BibtexDatabaseWriter(Writer writer, SavePreferences preferences, BibEntryTypesManager entryTypesManager) {
- super(writer, preferences, entryTypesManager);
+ public BibtexDatabaseWriter(BibWriter bibWriter, GeneralPreferences generalPreferences, SavePreferences savePreferences, BibEntryTypesManager entryTypesManager) {
+ super(bibWriter, generalPreferences, savePreferences, entryTypesManager);
+ }
+
+ public BibtexDatabaseWriter(Writer writer, String newline, GeneralPreferences generalPreferences, SavePreferences savePreferences, BibEntryTypesManager entryTypesManager) {
+ super(new BibWriter(writer, newline), generalPreferences, savePreferences, entryTypesManager);
}
@Override
protected void writeEpilogue(String epilogue) throws IOException {
if (!StringUtil.isNullOrEmpty(epilogue)) {
- writer.write(OS.NEWLINE);
- writer.write(epilogue);
- writer.write(OS.NEWLINE);
+ bibWriter.write(epilogue);
+ bibWriter.finishBlock();
}
}
@Override
protected void writeMetaDataItem(Map.Entry metaItem) throws IOException {
- writer.write(OS.NEWLINE);
- writer.write(COMMENT_PREFIX + "{");
- writer.write(MetaData.META_FLAG);
- writer.write(metaItem.getKey());
- writer.write(":");
- writer.write(metaItem.getValue());
- writer.write("}");
- writer.write(OS.NEWLINE);
+ bibWriter.write(COMMENT_PREFIX + "{");
+ bibWriter.write(MetaData.META_FLAG);
+ bibWriter.write(metaItem.getKey());
+ bibWriter.write(":");
+ bibWriter.write(metaItem.getValue());
+ bibWriter.write("}");
+ bibWriter.finishBlock();
}
@Override
protected void writePreamble(String preamble) throws IOException {
if (!StringUtil.isNullOrEmpty(preamble)) {
- writer.write(OS.NEWLINE);
- writer.write(PREAMBLE_PREFIX + "{");
- writer.write(preamble);
- writer.write('}' + OS.NEWLINE);
+ bibWriter.write(PREAMBLE_PREFIX + "{");
+ bibWriter.write(preamble);
+ bibWriter.writeLine("}");
+ bibWriter.finishBlock();
}
}
@Override
- protected void writeString(BibtexString bibtexString, boolean isFirstString, int maxKeyLength) throws IOException {
+ protected void writeString(BibtexString bibtexString, int maxKeyLength) throws IOException {
// If the string has not been modified, write it back as it was
- if (!preferences.shouldReformatFile() && !bibtexString.hasChanged()) {
- writer.write(bibtexString.getParsedSerialization());
+ if (!savePreferences.shouldReformatFile() && !bibtexString.hasChanged()) {
+ bibWriter.write(bibtexString.getParsedSerialization());
return;
}
// Write user comments
String userComments = bibtexString.getUserComments();
if (!userComments.isEmpty()) {
- writer.write(userComments + OS.NEWLINE);
- }
-
- if (isFirstString) {
- writer.write(OS.NEWLINE);
+ bibWriter.writeLine(userComments);
}
- writer.write(STRING_PREFIX + "{" + bibtexString.getName() + StringUtil
+ bibWriter.write(STRING_PREFIX + "{" + bibtexString.getName() + StringUtil
.repeatSpaces(maxKeyLength - bibtexString.getName().length()) + " = ");
if (bibtexString.getContent().isEmpty()) {
- writer.write("{}");
+ bibWriter.write("{}");
} else {
try {
- String formatted = new FieldWriter(preferences.getFieldWriterPreferences())
+ String formatted = new FieldWriter(savePreferences.getFieldWriterPreferences())
.write(InternalField.BIBTEX_STRING, bibtexString.getContent()
);
- writer.write(formatted);
+ bibWriter.write(formatted);
} catch (InvalidFieldValueException ex) {
throw new IOException(ex);
}
}
- writer.write("}" + OS.NEWLINE);
+ bibWriter.writeLine("}");
}
@Override
protected void writeEntryTypeDefinition(BibEntryType customType) throws IOException {
- writer.write(OS.NEWLINE);
- writer.write(COMMENT_PREFIX + "{");
- writer.write(BibEntryTypesManager.serialize(customType));
- writer.write("}");
- writer.write(OS.NEWLINE);
+ bibWriter.write(COMMENT_PREFIX + "{");
+ bibWriter.write(BibEntryTypesManager.serialize(customType));
+ bibWriter.writeLine("}");
+ bibWriter.finishBlock();
}
@Override
- protected void writePrelogue(BibDatabaseContext bibDatabaseContext, Charset encoding) throws IOException {
- if (encoding == null) {
+ protected void writeProlog(BibDatabaseContext bibDatabaseContext, Charset encoding) throws IOException {
+ if ((encoding == null) || (encoding == StandardCharsets.UTF_8)) {
return;
}
// Writes the file encoding information.
- writer.write("% ");
- writer.write(SavePreferences.ENCODING_PREFIX + encoding);
- writer.write(OS.NEWLINE);
+ bibWriter.write("% ");
+ bibWriter.writeLine(SavePreferences.ENCODING_PREFIX + encoding);
}
@Override
protected void writeDatabaseID(String sharedDatabaseID) throws IOException {
- writer.write("% " +
- DATABASE_ID_PREFIX +
- " " +
- sharedDatabaseID +
- OS.NEWLINE);
+ bibWriter.write("% ");
+ bibWriter.write(DATABASE_ID_PREFIX);
+ bibWriter.write(" ");
+ bibWriter.writeLine(sharedDatabaseID);
}
@Override
protected void writeEntry(BibEntry entry, BibDatabaseMode mode) throws IOException {
- BibEntryWriter bibtexEntryWriter = new BibEntryWriter(new FieldWriter(preferences.getFieldWriterPreferences()), entryTypesManager);
- bibtexEntryWriter.write(entry, writer, mode, preferences.shouldReformatFile());
+ BibEntryWriter bibtexEntryWriter = new BibEntryWriter(new FieldWriter(savePreferences.getFieldWriterPreferences()), entryTypesManager);
+ bibtexEntryWriter.write(entry, bibWriter, mode, savePreferences.shouldReformatFile());
}
}
diff --git a/src/main/java/org/jabref/logic/exporter/EmbeddedBibFilePdfExporter.java b/src/main/java/org/jabref/logic/exporter/EmbeddedBibFilePdfExporter.java
index e4735cfc2c2..ea8782474c9 100644
--- a/src/main/java/org/jabref/logic/exporter/EmbeddedBibFilePdfExporter.java
+++ b/src/main/java/org/jabref/logic/exporter/EmbeddedBibFilePdfExporter.java
@@ -15,6 +15,7 @@
import org.jabref.logic.bibtex.BibEntryWriter;
import org.jabref.logic.bibtex.FieldWriter;
import org.jabref.logic.bibtex.FieldWriterPreferences;
+import org.jabref.logic.util.OS;
import org.jabref.logic.util.StandardFileType;
import org.jabref.logic.util.io.FileUtil;
import org.jabref.model.database.BibDatabaseContext;
@@ -101,12 +102,13 @@ private void embedBibTex(String bibTeX, Path file, Charset encoding) throws IOEx
}
private String getBibString(List entries) throws IOException {
- StringWriter stringWriter = new StringWriter(200);
+ StringWriter stringWriter = new StringWriter();
+ BibWriter bibWriter = new BibWriter(stringWriter, OS.NEWLINE);
FieldWriter fieldWriter = FieldWriter.buildIgnoreHashes(fieldWriterPreferences);
BibEntryWriter bibEntryWriter = new BibEntryWriter(fieldWriter, bibEntryTypesManager);
for (BibEntry entry : entries) {
- bibEntryWriter.writeWithoutPrependedNewlines(entry, stringWriter, bibDatabaseMode);
+ bibEntryWriter.write(entry, bibWriter, bibDatabaseMode);
}
- return stringWriter.getBuffer().toString();
+ return stringWriter.toString();
}
}
diff --git a/src/main/java/org/jabref/logic/exporter/OOCalcDatabase.java b/src/main/java/org/jabref/logic/exporter/OOCalcDatabase.java
index cce17100805..6c8ce936464 100644
--- a/src/main/java/org/jabref/logic/exporter/OOCalcDatabase.java
+++ b/src/main/java/org/jabref/logic/exporter/OOCalcDatabase.java
@@ -1,10 +1,10 @@
package org.jabref.logic.exporter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
-import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.jabref.logic.bibtex.comparator.FieldComparator;
@@ -15,6 +15,7 @@
import org.jabref.model.database.BibDatabase;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.FieldFactory;
import org.jabref.model.entry.field.InternalField;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.field.UnknownField;
@@ -28,26 +29,21 @@
class OOCalcDatabase {
private static final Logger LOGGER = LoggerFactory.getLogger(OOCalcDatabase.class);
+ private static final Field REPORT_TYPE_FIELD = new UnknownField("reporttype");
- private final List entries;
+ private final List entries = new ArrayList<>();
+ private final List toExportFields = Stream.concat(FieldFactory.getStandardFieldsWithCitationKey().stream(), Stream.of(REPORT_TYPE_FIELD))
+ .collect(Collectors.toList());
public OOCalcDatabase(BibDatabase bibtex, List entries) {
- // Make a list of comparators for sorting the entries:
+ this.entries.addAll(entries != null ? entries : bibtex.getEntries());
+
List comparators = new ArrayList<>();
comparators.add(new FieldComparator(StandardField.AUTHOR));
comparators.add(new FieldComparator(StandardField.YEAR));
comparators.add(new FieldComparator(InternalField.KEY_FIELD));
- // Use glazed lists to get a sorted view of the entries:
- List entryList = new ArrayList<>();
- // Set up a list of all entries, if keySet==null, or the entries whose
- // ids are in keySet, otherwise:
- if (entries == null) {
- entryList.addAll(bibtex.getEntries());
- } else {
- entryList.addAll(entries);
- }
- Collections.sort(entryList, new FieldComparatorStack<>(comparators));
- this.entries = entryList;
+
+ this.entries.sort(new FieldComparatorStack<>(comparators));
}
private static String getField(BibEntry e, Field field) {
@@ -55,142 +51,83 @@ private static String getField(BibEntry e, Field field) {
}
public Document getDOMrepresentation() {
- Document result = null;
+ Document document = null;
try {
- DocumentBuilder dbuild = DocumentBuilderFactory.newInstance().newDocumentBuilder();
- result = dbuild.newDocument();
- Element collection = result.createElement("office:document-content");
- // collection.setAttribute("xmlns", "http://openoffice.org/2000/office");
- collection.setAttribute("xmlns:office", "http://openoffice.org/2000/office");
- collection.setAttribute("xmlns:style", "http://openoffice.org/2000/style");
- collection.setAttribute("xmlns:text", "http://openoffice.org/2000/text");
- collection.setAttribute("xmlns:table", "http://openoffice.org/2000/table");
- collection.setAttribute("xmlns:office:class", "spreadsheet");
- collection.setAttribute("xmlns:office:version", "1.0");
- collection.setAttribute("xmlns:fo", "http://www.w3.org/1999/XSL/Format");
- Element el = result.createElement("office:script");
- collection.appendChild(el);
-
- el = result.createElement("office:automatic-styles");
- Element el2 = result.createElement("style:style");
- el2.setAttribute("style:name", "ro1");
- el2.setAttribute("style:family", "table-row");
- Element el3 = result.createElement("style.properties");
- el3.setAttribute("style:row-height", "0.1681inch");
- el3.setAttribute("fo:break-before", "auto");
- el3.setAttribute("style:use-optimal-row-height", "true");
- el2.appendChild(el3);
- el.appendChild(el2);
- el2 = result.createElement("style:style");
- el2.setAttribute("style:name", "ta1");
- el2.setAttribute("style:family", "table");
- el2.setAttribute("style:master-page-name", "Default");
- el3 = result.createElement("style:properties");
- el3.setAttribute("table:display", "true");
- el2.appendChild(el3);
- el.appendChild(el2);
- collection.appendChild(el);
-
- Element body = result.createElement("office:body");
- Element table = result.createElement("table:table");
- table.setAttribute("table:name", "biblio");
- table.setAttribute("table.style-name", "ta1");
-
- Element row = result.createElement("table:table-row");
- row.setAttribute("table.style-name", "ro1");
- addTableCell(result, row, "Type");
- addTableCell(result, row, "ISBN");
- addTableCell(result, row, "Identifier");
- addTableCell(result, row, "Author");
- addTableCell(result, row, "Title");
- addTableCell(result, row, "Journal");
- addTableCell(result, row, "Volume");
- addTableCell(result, row, "Number");
- addTableCell(result, row, "Month");
- addTableCell(result, row, "Pages");
- addTableCell(result, row, "Year");
- addTableCell(result, row, "Address");
- addTableCell(result, row, "Note");
- addTableCell(result, row, "URL");
- addTableCell(result, row, "Booktitle");
- addTableCell(result, row, "Chapter");
- addTableCell(result, row, "Edition");
- addTableCell(result, row, "Series");
- addTableCell(result, row, "Editor");
- addTableCell(result, row, "Publisher");
- addTableCell(result, row, "ReportType");
- addTableCell(result, row, "Howpublished");
- addTableCell(result, row, "Institution");
- addTableCell(result, row, "Organization");
- addTableCell(result, row, "School");
- addTableCell(result, row, "Annote");
- addTableCell(result, row, "Assignee");
- addTableCell(result, row, "Day");
- addTableCell(result, row, "Dayfiled");
- addTableCell(result, row, "Monthfiled");
- addTableCell(result, row, "Yearfiled");
- addTableCell(result, row, "Language");
- addTableCell(result, row, "Nationality");
- addTableCell(result, row, "Revision");
- addTableCell(result, row, "Custom1");
- addTableCell(result, row, "Custom2");
- addTableCell(result, row, "Custom3");
- addTableCell(result, row, "Custom4");
- addTableCell(result, row, "Custom5");
- table.appendChild(row);
-
- for (BibEntry e : entries) {
- row = result.createElement("table:table-row");
- addTableCell(result, row, new GetOpenOfficeType().format(e.getType().getName()));
- addTableCell(result, row, getField(e, StandardField.ISBN));
- addTableCell(result, row, getField(e, InternalField.KEY_FIELD));
- addTableCell(result, row, getField(e, StandardField.AUTHOR)); // new AuthorLastFirst().format(getField(e, StandardField.AUTHOR_FIELD)));
- addTableCell(result, row, new RemoveWhitespace().format(new RemoveBrackets().format(getField(e, StandardField.TITLE))));
- addTableCell(result, row, getField(e, StandardField.JOURNAL));
- addTableCell(result, row, getField(e, StandardField.VOLUME));
- addTableCell(result, row, getField(e, StandardField.NUMBER));
- addTableCell(result, row, getField(e, StandardField.MONTH));
- addTableCell(result, row, getField(e, StandardField.PAGES));
- addTableCell(result, row, getField(e, StandardField.YEAR));
- addTableCell(result, row, getField(e, StandardField.ADDRESS));
- addTableCell(result, row, getField(e, StandardField.NOTE));
- addTableCell(result, row, getField(e, StandardField.URL));
- addTableCell(result, row, getField(e, StandardField.BOOKTITLE));
- addTableCell(result, row, getField(e, StandardField.CHAPTER));
- addTableCell(result, row, getField(e, StandardField.EDITION));
- addTableCell(result, row, getField(e, StandardField.SERIES));
- addTableCell(result, row, getField(e, StandardField.EDITOR)); // new AuthorLastFirst().format(getField(e, StandardField.EDITOR_FIELD)));
- addTableCell(result, row, getField(e, StandardField.PUBLISHER));
- addTableCell(result, row, getField(e, new UnknownField("reporttype")));
- addTableCell(result, row, getField(e, StandardField.HOWPUBLISHED));
- addTableCell(result, row, getField(e, StandardField.INSTITUTION));
- addTableCell(result, row, getField(e, StandardField.ORGANIZATION));
- addTableCell(result, row, getField(e, StandardField.SCHOOL));
- addTableCell(result, row, getField(e, StandardField.ANNOTE));
- addTableCell(result, row, getField(e, StandardField.ASSIGNEE));
- addTableCell(result, row, getField(e, StandardField.DAY));
- addTableCell(result, row, getField(e, StandardField.DAYFILED));
- addTableCell(result, row, getField(e, StandardField.MONTHFILED));
- addTableCell(result, row, getField(e, StandardField.YEARFILED));
- addTableCell(result, row, getField(e, StandardField.LANGUAGE));
- addTableCell(result, row, getField(e, StandardField.NATIONALITY));
- addTableCell(result, row, getField(e, StandardField.REVISION));
- addTableCell(result, row, "");
- addTableCell(result, row, "");
- addTableCell(result, row, "");
- addTableCell(result, row, "");
- addTableCell(result, row, "");
- table.appendChild(row);
- }
+ document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ Element root = createRootElement(document);
+ Element body = document.createElement("office:body");
+ Element table = createTableElement(document);
body.appendChild(table);
- collection.appendChild(body);
+ root.appendChild(body);
+ document.appendChild(root);
+
+ addTableHeader(table, document);
- result.appendChild(collection);
+ for (BibEntry entry : entries) {
+ addEntryRow(entry, table, document);
+ }
} catch (Exception e) {
LOGGER.warn("Exception caught...", e);
}
- return result;
+ return document;
+ }
+
+ private void addEntryRow(BibEntry entry, Element table, Document document) {
+ final Element row = document.createElement("table:table-row");
+
+ addTableCell(document, row, new GetOpenOfficeType().format(entry.getType().getName()));
+ toExportFields.forEach(field -> {
+ if (field.equals(StandardField.TITLE)) {
+ addTableCell(document, row, new RemoveWhitespace().format(new RemoveBrackets().format(getField(entry, StandardField.TITLE))));
+ } else {
+ addTableCell(document, row, getField(entry, field));
+ }
+ });
+
+ table.appendChild(row);
+ }
+
+ private Element createTableElement(Document document) {
+ Element table = document.createElement("table:table");
+ table.setAttribute("table:name", "biblio");
+ table.setAttribute("table.style-name", "ta1");
+ return table;
+ }
+
+ private Element createRootElement(Document document) {
+ Element root = document.createElement("office:document-content");
+ root.setAttribute("xmlns:office", "http://openoffice.org/2000/office");
+ root.setAttribute("xmlns:style", "http://openoffice.org/2000/style");
+ root.setAttribute("xmlns:text", "http://openoffice.org/2000/text");
+ root.setAttribute("xmlns:table", "http://openoffice.org/2000/table");
+ root.setAttribute("xmlns:office:class", "spreadsheet");
+ root.setAttribute("xmlns:office:version", "1.0");
+ root.setAttribute("xmlns:fo", "http://www.w3.org/1999/XSL/Format");
+ Element el = document.createElement("office:script");
+ root.appendChild(el);
+
+ el = document.createElement("office:automatic-styles");
+ Element el2 = document.createElement("style:style");
+ el2.setAttribute("style:name", "ro1");
+ el2.setAttribute("style:family", "table-row");
+ Element el3 = document.createElement("style.properties");
+ el3.setAttribute("style:row-height", "0.1681inch");
+ el3.setAttribute("fo:break-before", "auto");
+ el3.setAttribute("style:use-optimal-row-height", "true");
+ el2.appendChild(el3);
+ el.appendChild(el2);
+ el2 = document.createElement("style:style");
+ el2.setAttribute("style:name", "ta1");
+ el2.setAttribute("style:family", "table");
+ el2.setAttribute("style:master-page-name", "Default");
+ el3 = document.createElement("style:properties");
+ el3.setAttribute("table:display", "true");
+ el2.appendChild(el3);
+ el.appendChild(el2);
+ root.appendChild(el);
+
+ return root;
}
private static void addTableCell(Document doc, Element parent, String content) {
@@ -198,8 +135,18 @@ private static void addTableCell(Document doc, Element parent, String content) {
Element text = doc.createElement("text:p");
Text textNode = doc.createTextNode(content);
text.appendChild(textNode);
- // text.setTextContent(content);
cell.appendChild(text);
parent.appendChild(cell);
}
+
+ private void addTableHeader(Element table, Document document) {
+ Element firstRow = document.createElement("table:table-row");
+ firstRow.setAttribute("table.style-name", "ro1");
+ addTableCell(document, firstRow, "Type");
+ for (Field field : toExportFields) {
+ addTableCell(document, firstRow, field.getDisplayName());
+ }
+
+ table.appendChild(firstRow);
+ }
}
diff --git a/src/main/java/org/jabref/logic/exporter/SavePreferences.java b/src/main/java/org/jabref/logic/exporter/SavePreferences.java
index a366542b5f9..1165d3338c9 100644
--- a/src/main/java/org/jabref/logic/exporter/SavePreferences.java
+++ b/src/main/java/org/jabref/logic/exporter/SavePreferences.java
@@ -1,7 +1,5 @@
package org.jabref.logic.exporter;
-import java.nio.charset.Charset;
-
import org.jabref.logic.bibtex.FieldWriterPreferences;
import org.jabref.logic.citationkeypattern.CitationKeyPatternPreferences;
import org.jabref.model.metadata.SaveOrderConfig;
@@ -16,7 +14,6 @@ public enum DatabaseSaveType { ALL, PLAIN_BIBTEX }
private final boolean reformatFile;
private boolean saveInOriginalOrder;
private SaveOrderConfig saveOrder;
- private Charset encoding;
private boolean makeBackup;
private DatabaseSaveType saveType;
private boolean takeMetadataSaveOrderInAccount;
@@ -25,7 +22,6 @@ public enum DatabaseSaveType { ALL, PLAIN_BIBTEX }
private SavePreferences(Boolean saveInOriginalOrder,
SaveOrderConfig saveOrder,
- Charset encoding,
Boolean makeBackup,
DatabaseSaveType saveType,
Boolean takeMetadataSaveOrderInAccount,
@@ -35,7 +31,6 @@ private SavePreferences(Boolean saveInOriginalOrder,
this.saveInOriginalOrder = saveInOriginalOrder;
this.saveOrder = saveOrder;
- this.encoding = encoding;
this.makeBackup = makeBackup;
this.saveType = saveType;
this.takeMetadataSaveOrderInAccount = takeMetadataSaveOrderInAccount;
@@ -46,7 +41,6 @@ private SavePreferences(Boolean saveInOriginalOrder,
public SavePreferences(Boolean saveInOriginalOrder,
SaveOrderConfig saveOrder,
- Charset encoding,
DatabaseSaveType saveType,
Boolean takeMetadataSaveOrderInAccount,
Boolean reformatFile,
@@ -55,7 +49,6 @@ public SavePreferences(Boolean saveInOriginalOrder,
this(saveInOriginalOrder,
saveOrder,
- encoding,
true,
saveType,
takeMetadataSaveOrderInAccount,
@@ -105,15 +98,6 @@ public SavePreferences withMakeBackup(Boolean newMakeBackup) {
return this;
}
- public Charset getEncoding() {
- return encoding;
- }
-
- public SavePreferences withEncoding(Charset newEncoding) {
- this.encoding = newEncoding;
- return this;
- }
-
public DatabaseSaveType getSaveType() {
return saveType;
}
diff --git a/src/main/java/org/jabref/logic/externalfiles/ExternalFilesContentImporter.java b/src/main/java/org/jabref/logic/externalfiles/ExternalFilesContentImporter.java
index 7c0265bc8a6..b990eb36b29 100644
--- a/src/main/java/org/jabref/logic/externalfiles/ExternalFilesContentImporter.java
+++ b/src/main/java/org/jabref/logic/externalfiles/ExternalFilesContentImporter.java
@@ -10,13 +10,16 @@
import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.importer.fileformat.PdfMergeMetadataImporter;
import org.jabref.model.util.FileUpdateMonitor;
+import org.jabref.preferences.GeneralPreferences;
public class ExternalFilesContentImporter {
+ private final GeneralPreferences generalPreferences;
private final ImporterPreferences importerPreferences;
private final ImportFormatPreferences importFormatPreferences;
- public ExternalFilesContentImporter(ImporterPreferences importerPreferences, ImportFormatPreferences importFormatPreferences) {
+ public ExternalFilesContentImporter(GeneralPreferences generalPreferences, ImporterPreferences importerPreferences, ImportFormatPreferences importFormatPreferences) {
+ this.generalPreferences = generalPreferences;
this.importerPreferences = importerPreferences;
this.importFormatPreferences = importFormatPreferences;
}
@@ -30,6 +33,6 @@ public ParserResult importPDFContent(Path file) {
}
public ParserResult importFromBibFile(Path bibFile, FileUpdateMonitor fileUpdateMonitor) throws IOException {
- return OpenDatabase.loadDatabase(bibFile, importFormatPreferences, fileUpdateMonitor);
+ return OpenDatabase.loadDatabase(bibFile, generalPreferences, importFormatPreferences, fileUpdateMonitor);
}
}
diff --git a/src/main/java/org/jabref/logic/importer/CompositeIdFetcher.java b/src/main/java/org/jabref/logic/importer/CompositeIdFetcher.java
new file mode 100644
index 00000000000..652cb52255d
--- /dev/null
+++ b/src/main/java/org/jabref/logic/importer/CompositeIdFetcher.java
@@ -0,0 +1,47 @@
+package org.jabref.logic.importer;
+
+import java.util.Optional;
+
+import org.jabref.logic.importer.fetcher.ArXiv;
+import org.jabref.logic.importer.fetcher.DoiFetcher;
+import org.jabref.logic.importer.fetcher.IacrEprintFetcher;
+import org.jabref.logic.importer.fetcher.IsbnFetcher;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.identifier.ArXivIdentifier;
+import org.jabref.model.entry.identifier.DOI;
+import org.jabref.model.entry.identifier.ISBN;
+import org.jabref.model.entry.identifier.IacrEprint;
+
+public class CompositeIdFetcher {
+
+ private final ImportFormatPreferences importFormatPreferences;
+
+ public CompositeIdFetcher(ImportFormatPreferences importFormatPreferences) {
+ this.importFormatPreferences = importFormatPreferences;
+ }
+
+ public Optional performSearchById(String identifier) throws FetcherException {
+ Optional doi = DOI.parse(identifier);
+ if (doi.isPresent()) {
+ return new DoiFetcher(importFormatPreferences).performSearchById(doi.get().getNormalized());
+ }
+ Optional arXivIdentifier = ArXivIdentifier.parse(identifier);
+ if (arXivIdentifier.isPresent()) {
+ return new ArXiv(importFormatPreferences).performSearchById(arXivIdentifier.get().getNormalized());
+ }
+ Optional isbn = ISBN.parse(identifier);
+ if (isbn.isPresent()) {
+ return new IsbnFetcher(importFormatPreferences).performSearchById(isbn.get().getNormalized());
+ }
+ Optional iacrEprint = IacrEprint.parse(identifier);
+ if (iacrEprint.isPresent()) {
+ return new IacrEprintFetcher(importFormatPreferences).performSearchById(iacrEprint.get().getNormalized());
+ }
+
+ return Optional.empty();
+ }
+
+ public String getName() {
+ return "CompositeIdFetcher";
+ }
+}
diff --git a/src/main/java/org/jabref/logic/importer/ImportFormatPreferences.java b/src/main/java/org/jabref/logic/importer/ImportFormatPreferences.java
index 6582ced5a75..3076dffdccb 100644
--- a/src/main/java/org/jabref/logic/importer/ImportFormatPreferences.java
+++ b/src/main/java/org/jabref/logic/importer/ImportFormatPreferences.java
@@ -1,35 +1,43 @@
package org.jabref.logic.importer;
-import java.nio.charset.Charset;
import java.util.Set;
import org.jabref.logic.bibtex.FieldContentFormatterPreferences;
import org.jabref.logic.citationkeypattern.CitationKeyPatternPreferences;
import org.jabref.logic.importer.fileformat.CustomImporter;
+import org.jabref.logic.preferences.DOIPreferences;
import org.jabref.logic.xmp.XmpPreferences;
public class ImportFormatPreferences {
private final Set customImportList;
- private final Charset encoding;
private final Character keywordSeparator;
private final CitationKeyPatternPreferences citationKeyPatternPreferences;
private final FieldContentFormatterPreferences fieldContentFormatterPreferences;
private final XmpPreferences xmpPreferences;
+ private final DOIPreferences doiPreferences;
private final boolean keywordSyncEnabled;
- public ImportFormatPreferences(Set customImportList, Charset encoding, Character keywordSeparator,
+ public ImportFormatPreferences(Set customImportList,
+ Character keywordSeparator,
CitationKeyPatternPreferences citationKeyPatternPreferences,
- FieldContentFormatterPreferences fieldContentFormatterPreferences, XmpPreferences xmpPreferences, boolean keywordSyncEnabled) {
+ FieldContentFormatterPreferences fieldContentFormatterPreferences,
+ XmpPreferences xmpPreferences,
+ DOIPreferences doiPreferences,
+ boolean keywordSyncEnabled) {
this.customImportList = customImportList;
- this.encoding = encoding;
this.keywordSeparator = keywordSeparator;
this.citationKeyPatternPreferences = citationKeyPatternPreferences;
this.fieldContentFormatterPreferences = fieldContentFormatterPreferences;
this.xmpPreferences = xmpPreferences;
+ this.doiPreferences = doiPreferences;
this.keywordSyncEnabled = keywordSyncEnabled;
}
+ public DOIPreferences getDoiPreferences() {
+ return doiPreferences;
+ }
+
/**
* @deprecated importer should not know about the other custom importers
*/
@@ -38,10 +46,6 @@ public Set getCustomImportList() {
return customImportList;
}
- public Charset getEncoding() {
- return encoding;
- }
-
public Character getKeywordSeparator() {
return keywordSeparator;
}
@@ -54,11 +58,6 @@ public FieldContentFormatterPreferences getFieldContentFormatterPreferences() {
return fieldContentFormatterPreferences;
}
- public ImportFormatPreferences withEncoding(Charset newEncoding) {
- return new ImportFormatPreferences(customImportList, newEncoding, keywordSeparator, citationKeyPatternPreferences,
- fieldContentFormatterPreferences, xmpPreferences, keywordSyncEnabled);
- }
-
/**
* @deprecated importer should not keyword synchronization; this is a post-import action
*/
diff --git a/src/main/java/org/jabref/logic/importer/ImportFormatReader.java b/src/main/java/org/jabref/logic/importer/ImportFormatReader.java
index 1e023ebf258..e7c10b96f09 100644
--- a/src/main/java/org/jabref/logic/importer/ImportFormatReader.java
+++ b/src/main/java/org/jabref/logic/importer/ImportFormatReader.java
@@ -38,6 +38,7 @@
import org.jabref.model.entry.BibEntry;
import org.jabref.model.strings.StringUtil;
import org.jabref.model.util.FileUpdateMonitor;
+import org.jabref.preferences.GeneralPreferences;
public class ImportFormatReader {
@@ -49,9 +50,11 @@ public class ImportFormatReader {
*/
private final List formats = new ArrayList<>();
+ private GeneralPreferences generalPreferences;
private ImportFormatPreferences importFormatPreferences;
- public void resetImportFormats(ImporterPreferences importerPreferences, ImportFormatPreferences newImportFormatPreferences, XmpPreferences xmpPreferences, FileUpdateMonitor fileMonitor) {
+ public void resetImportFormats(ImporterPreferences importerPreferences, GeneralPreferences generalPreferences, ImportFormatPreferences newImportFormatPreferences, XmpPreferences xmpPreferences, FileUpdateMonitor fileMonitor) {
+ this.generalPreferences = generalPreferences;
this.importFormatPreferences = newImportFormatPreferences;
formats.clear();
@@ -112,7 +115,7 @@ public ParserResult importFromFile(String format, Path file) throws ImportExcept
}
try {
- return importer.get().importDatabase(file, importFormatPreferences.getEncoding());
+ return importer.get().importDatabase(file, generalPreferences.getDefaultEncoding());
} catch (IOException e) {
throw new ImportException(e);
}
@@ -179,15 +182,15 @@ public UnknownFormatImport importUnknownFormat(Path filePath, FileUpdateMonitor
Objects.requireNonNull(filePath);
try {
- UnknownFormatImport unknownFormatImport = importUnknownFormat(importer -> importer.importDatabase(filePath, importFormatPreferences.getEncoding()), importer -> importer.isRecognizedFormat(filePath, importFormatPreferences.getEncoding()));
- unknownFormatImport.parserResult.setFile(filePath.toFile());
+ UnknownFormatImport unknownFormatImport = importUnknownFormat(importer -> importer.importDatabase(filePath, generalPreferences.getDefaultEncoding()), importer -> importer.isRecognizedFormat(filePath, generalPreferences.getDefaultEncoding()));
+ unknownFormatImport.parserResult.setPath(filePath);
return unknownFormatImport;
} catch (ImportException e) {
// If all importers fail, try to read the file as BibTeX
try {
- ParserResult parserResult = OpenDatabase.loadDatabase(filePath, importFormatPreferences, fileMonitor);
+ ParserResult parserResult = OpenDatabase.loadDatabase(filePath, generalPreferences, importFormatPreferences, fileMonitor);
if (parserResult.getDatabase().hasEntries() || !parserResult.getDatabase().hasNoStrings()) {
- parserResult.setFile(filePath.toFile());
+ parserResult.setPath(filePath);
return new UnknownFormatImport(ImportFormatReader.BIBTEX_FORMAT, parserResult);
} else {
throw new ImportException(Localization.lang("Could not find a suitable import format."));
diff --git a/src/main/java/org/jabref/logic/importer/Importer.java b/src/main/java/org/jabref/logic/importer/Importer.java
index 120e23b99a5..1c790537677 100644
--- a/src/main/java/org/jabref/logic/importer/Importer.java
+++ b/src/main/java/org/jabref/logic/importer/Importer.java
@@ -1,14 +1,15 @@
package org.jabref.logic.importer;
import java.io.BufferedReader;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
import java.util.Objects;
import org.jabref.logic.util.FileType;
@@ -86,7 +87,7 @@ public ParserResult importDatabase(Path filePath, Charset encoding) throws IOExc
try (BufferedReader bufferedReader = getReader(filePath, encoding)) {
ParserResult parserResult = importDatabase(bufferedReader);
parserResult.getMetaData().setEncoding(encoding);
- parserResult.setFile(filePath.toFile());
+ parserResult.setPath(filePath);
// Make sure the mode is always set
if (parserResult.getMetaData().getMode().isEmpty()) {
@@ -123,7 +124,7 @@ protected static BufferedReader getUTF16Reader(Path filePath) throws IOException
public static BufferedReader getReader(Path filePath, Charset encoding)
throws IOException {
- InputStream stream = new FileInputStream(filePath.toFile());
+ InputStream stream = Files.newInputStream(filePath, StandardOpenOption.READ);
return new BufferedReader(new InputStreamReader(stream, encoding));
}
diff --git a/src/main/java/org/jabref/logic/importer/OpenDatabase.java b/src/main/java/org/jabref/logic/importer/OpenDatabase.java
index fbcfd11a880..bbc4796a22d 100644
--- a/src/main/java/org/jabref/logic/importer/OpenDatabase.java
+++ b/src/main/java/org/jabref/logic/importer/OpenDatabase.java
@@ -11,6 +11,7 @@
import org.jabref.migrations.PostOpenMigration;
import org.jabref.migrations.SpecialFieldsToSeparateFields;
import org.jabref.model.util.FileUpdateMonitor;
+import org.jabref.preferences.GeneralPreferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -28,10 +29,10 @@ private OpenDatabase() {
* @param fileToOpen Name of the BIB-file to open
* @return ParserResult which never is null
*/
- public static ParserResult loadDatabase(Path fileToOpen, ImportFormatPreferences importFormatPreferences, FileUpdateMonitor fileMonitor)
+ public static ParserResult loadDatabase(Path fileToOpen, GeneralPreferences generalPreferences, ImportFormatPreferences importFormatPreferences, FileUpdateMonitor fileMonitor)
throws IOException {
ParserResult result = new BibtexImporter(importFormatPreferences, fileMonitor).importDatabase(fileToOpen,
- importFormatPreferences.getEncoding());
+ generalPreferences.getDefaultEncoding());
performLoadDatabaseMigrations(result, importFormatPreferences.getKeywordSeparator());
diff --git a/src/main/java/org/jabref/logic/importer/ParserResult.java b/src/main/java/org/jabref/logic/importer/ParserResult.java
index 4df97e81535..2f134224562 100644
--- a/src/main/java/org/jabref/logic/importer/ParserResult.java
+++ b/src/main/java/org/jabref/logic/importer/ParserResult.java
@@ -1,6 +1,5 @@
package org.jabref.logic.importer;
-import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -96,17 +95,8 @@ public Optional getPath() {
return Optional.ofNullable(file);
}
- /**
- * @return the file object of the database file
- * @deprecated use {@link #getPath()}} instead
- */
- @Deprecated
- public Optional getFile() {
- return Optional.ofNullable(file).map(Path::toFile);
- }
-
- public void setFile(File f) {
- file = f.toPath();
+ public void setPath(Path path) {
+ file = path;
}
/**
diff --git a/src/main/java/org/jabref/logic/importer/WebFetchers.java b/src/main/java/org/jabref/logic/importer/WebFetchers.java
index 7ad07379070..efe64b8b3a7 100644
--- a/src/main/java/org/jabref/logic/importer/WebFetchers.java
+++ b/src/main/java/org/jabref/logic/importer/WebFetchers.java
@@ -164,8 +164,10 @@ public static SortedSet> getIdFetchers(ImportFor
*/
public static Set getFullTextFetchers(ImportFormatPreferences importFormatPreferences) {
Set fetchers = new HashSet<>();
+
// Original
- fetchers.add(new DoiResolution());
+ fetchers.add(new DoiResolution(importFormatPreferences.getDoiPreferences()));
+
// Publishers
fetchers.add(new ScienceDirect());
fetchers.add(new SpringerLink());
@@ -173,6 +175,7 @@ public static Set getFullTextFetchers(ImportFormatPreferences i
fetchers.add(new ArXiv(importFormatPreferences));
fetchers.add(new IEEE(importFormatPreferences));
fetchers.add(new ApsFetcher());
+
// Meta search
// fetchers.add(new JstorFetcher(importFormatPreferences));
// fetchers.add(new GoogleScholar(importFormatPreferences));
diff --git a/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java
index 617c0985814..41afea25707 100644
--- a/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java
+++ b/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java
@@ -62,6 +62,7 @@ public Optional getHelpPage() {
@Override
public Optional performSearchById(String identifier) throws FetcherException {
Optional doi = DOI.parse(identifier);
+
try {
if (doi.isPresent()) {
Optional fetchedEntry;
@@ -70,13 +71,18 @@ public Optional performSearchById(String identifier) throws FetcherExc
if (getAgency(doi.get()).isPresent() && "medra".equalsIgnoreCase(getAgency(doi.get()).get())) {
return new Medra().performSearchById(identifier);
}
-
URL doiURL = new URL(doi.get().getURIAsASCIIString());
// BibTeX data
URLDownload download = getUrlDownload(doiURL);
download.addHeader("Accept", MediaTypes.APPLICATION_BIBTEX);
- String bibtexString = download.asString();
+ String bibtexString;
+ try {
+ bibtexString = download.asString();
+ } catch (IOException e) {
+ // an IOException will be thrown if download is unable to download from the doiURL
+ throw new FetcherException(Localization.lang("No DOI data exists"), e);
+ }
// BibTeX entry
fetchedEntry = BibtexParser.singleFromString(bibtexString, preferences, new DummyFileUpdateMonitor());
diff --git a/src/main/java/org/jabref/logic/importer/fetcher/DoiResolution.java b/src/main/java/org/jabref/logic/importer/fetcher/DoiResolution.java
index f43eb1825b5..8a2ba23d13b 100644
--- a/src/main/java/org/jabref/logic/importer/fetcher/DoiResolution.java
+++ b/src/main/java/org/jabref/logic/importer/fetcher/DoiResolution.java
@@ -2,6 +2,8 @@
import java.io.IOException;
import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@@ -12,6 +14,7 @@
import org.jabref.logic.importer.FulltextFetcher;
import org.jabref.logic.net.URLDownload;
+import org.jabref.logic.preferences.DOIPreferences;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.identifier.DOI;
@@ -27,9 +30,19 @@
/**
* FulltextFetcher implementation that follows the DOI resolution redirects and scans for a full-text PDF URL.
+ *
+ * Note that we also have custom fetchers in place.
+ * See {@link org.jabref.logic.importer.WebFetchers#getFullTextFetchers(org.jabref.logic.importer.ImportFormatPreferences)}.
*/
public class DoiResolution implements FulltextFetcher {
+
private static final Logger LOGGER = LoggerFactory.getLogger(DoiResolution.class);
+ private DOIPreferences doiPreferences;
+
+ public DoiResolution(DOIPreferences doiPreferences) {
+ super();
+ this.doiPreferences = doiPreferences;
+ }
@Override
public Optional findFullText(BibEntry entry) throws IOException {
@@ -37,11 +50,23 @@ public Optional findFullText(BibEntry entry) throws IOException {
Optional doi = entry.getField(StandardField.DOI).flatMap(DOI::parse);
- if (!doi.isPresent()) {
+ if (doi.isEmpty()) {
return Optional.empty();
}
- String doiLink = doi.get().getURIAsASCIIString();
+ URL base;
+
+ String doiLink;
+ if (doiPreferences.isUseCustom()) {
+ base = new URL(doiPreferences.getDefaultBaseURI());
+ doiLink = doi.get()
+ .getExternalURIWithCustomBase(base.toString())
+ .map(URI::toASCIIString)
+ .orElse("");
+ } else {
+ base = DOI.RESOLVER.toURL();
+ doiLink = doi.get().getURIAsASCIIString();
+ }
if (doiLink.isEmpty()) {
return Optional.empty();
}
@@ -51,7 +76,7 @@ public Optional findFullText(BibEntry entry) throws IOException {
Connection connection = Jsoup.connect(doiLink);
// pretend to be a browser (agent & referrer)
connection.userAgent(URLDownload.USER_AGENT);
- connection.referrer("http://www.google.com");
+ connection.referrer("https://www.google.com");
connection.followRedirects(true);
connection.ignoreHttpErrors(true);
// some publishers are quite slow (default is 3s)
@@ -65,6 +90,10 @@ public Optional findFullText(BibEntry entry) throws IOException {
if (citationMetaTag.isPresent()) {
return citationMetaTag;
}
+ Optional embeddedLink = findEmbeddedLink(html, base);
+ if (embeddedLink.isPresent()) {
+ return embeddedLink;
+ }
// scan for PDF
Elements hrefElements = html.body().select("a[href]");
@@ -124,6 +153,23 @@ private Optional citationMetaTag(Document html) {
return Optional.empty();
}
+ private Optional findEmbeddedLink(Document html, URL base) {
+ Elements embedElement = html.body().select("embed[id='pdf']");
+ Optional pdfUrl = embedElement
+ .stream()
+ .map(e -> e.attr("src")).findFirst();
+
+ if (pdfUrl.isPresent()) {
+ try {
+ URL url = base.toURI().resolve(pdfUrl.get()).toURL();
+ return Optional.of(url);
+ } catch (MalformedURLException | URISyntaxException e) {
+ return Optional.empty();
+ }
+ }
+ return Optional.empty();
+ }
+
private Optional findDistinctLinks(List urls) {
List distinctLinks = urls.stream().distinct().collect(Collectors.toList());
diff --git a/src/main/java/org/jabref/logic/importer/fetcher/IEEE.java b/src/main/java/org/jabref/logic/importer/fetcher/IEEE.java
index 77b3c626cd0..a5e320a92ff 100644
--- a/src/main/java/org/jabref/logic/importer/fetcher/IEEE.java
+++ b/src/main/java/org/jabref/logic/importer/fetcher/IEEE.java
@@ -270,8 +270,6 @@ public URL getURLForQuery(QueryNode luceneQuery, int pageNumber) throws URISynta
// Starts to index at 1 for the first entry
uriBuilder.addParameter("start_record", String.valueOf(getPageSize() * pageNumber) + 1);
- URLDownload.bypassSSLVerification();
-
return uriBuilder.build().toURL();
}
}
diff --git a/src/main/java/org/jabref/logic/importer/fetcher/MrDLibFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/MrDLibFetcher.java
index bbe4ad1c27c..5d9318edebc 100644
--- a/src/main/java/org/jabref/logic/importer/fetcher/MrDLibFetcher.java
+++ b/src/main/java/org/jabref/logic/importer/fetcher/MrDLibFetcher.java
@@ -98,7 +98,6 @@ public String getDescription() {
private String makeServerRequest(String queryByTitle) throws FetcherException {
try {
URLDownload urlDownload = new URLDownload(constructQuery(queryByTitle));
- URLDownload.bypassSSLVerification();
String response = urlDownload.asString();
// Conversion of < and >
diff --git a/src/main/java/org/jabref/logic/importer/fetcher/ZbMATH.java b/src/main/java/org/jabref/logic/importer/fetcher/ZbMATH.java
index 592688412ce..d3fe4a954a3 100644
--- a/src/main/java/org/jabref/logic/importer/fetcher/ZbMATH.java
+++ b/src/main/java/org/jabref/logic/importer/fetcher/ZbMATH.java
@@ -18,7 +18,6 @@
import org.jabref.logic.importer.SearchBasedParserFetcher;
import org.jabref.logic.importer.fetcher.transformers.ZbMathQueryTransformer;
import org.jabref.logic.importer.fileformat.BibtexParser;
-import org.jabref.logic.net.URLDownload;
import org.jabref.model.entry.AuthorList;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.StandardField;
@@ -110,9 +109,6 @@ public URL getURLForQuery(QueryNode luceneQuery) throws URISyntaxException, Malf
uriBuilder.addParameter("q", new ZbMathQueryTransformer().transformLuceneQuery(luceneQuery).orElse("")); // search all fields
uriBuilder.addParameter("start", "0"); // start index
uriBuilder.addParameter("count", "200"); // should return up to 200 items (instead of default 100)
-
- URLDownload.bypassSSLVerification();
-
return uriBuilder.build().toURL();
}
@@ -123,9 +119,6 @@ public URL getUrlForIdentifier(String identifier) throws URISyntaxException, Mal
uriBuilder.addParameter("q", query);
uriBuilder.addParameter("start", "0"); // start index
uriBuilder.addParameter("count", "1"); // return exactly one item
-
- URLDownload.bypassSSLVerification();
-
return uriBuilder.build().toURL();
}
diff --git a/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java b/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java
index eb348459668..c1f75f9dcba 100644
--- a/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java
+++ b/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java
@@ -6,6 +6,7 @@
import java.io.InputStreamReader;
import java.io.PushbackReader;
import java.io.Reader;
+import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Deque;
@@ -29,6 +30,7 @@
import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.importer.util.MetaDataParser;
import org.jabref.logic.l10n.Localization;
+import org.jabref.logic.util.OS;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.KeyCollisionException;
import org.jabref.model.entry.BibEntry;
@@ -64,7 +66,7 @@
public class BibtexParser implements Parser {
private static final Logger LOGGER = LoggerFactory.getLogger(BibtexParser.class);
- private static final Integer LOOKAHEAD = 64;
+ private static final Integer LOOKAHEAD = 1024;
private final FieldContentFormatter fieldContentFormatter;
private final Deque pureTextFromFile = new LinkedList<>();
private final ImportFormatPreferences importFormatPreferences;
@@ -132,8 +134,10 @@ public ParserResult parse(Reader in) throws IOException {
Objects.requireNonNull(in);
pushbackReader = new PushbackReader(in, BibtexParser.LOOKAHEAD);
- // Bibtex related contents.
- initializeParserResult();
+ String newLineSeparator = determineNewLineSeparator();
+
+ // BibTeX related contents.
+ initializeParserResult(newLineSeparator);
parseDatabaseID();
@@ -142,14 +146,36 @@ public ParserResult parse(Reader in) throws IOException {
return parseFileContent();
}
- private void initializeParserResult() {
+ private String determineNewLineSeparator() throws IOException {
+ String newLineSeparator = OS.NEWLINE;
+ StringWriter stringWriter = new StringWriter(BibtexParser.LOOKAHEAD);
+ int i = 0;
+ int currentChar;
+ do {
+ currentChar = pushbackReader.read();
+ stringWriter.append((char) currentChar);
+ i++;
+ } while ((i < BibtexParser.LOOKAHEAD) && (currentChar != '\r') && (currentChar != '\n'));
+ if (currentChar == '\r') {
+ newLineSeparator = "\r\n";
+ } else if (currentChar == '\n') {
+ newLineSeparator = "\n";
+ }
+
+ // unread all sneaked characters
+ pushbackReader.unread(stringWriter.toString().toCharArray());
+
+ return newLineSeparator;
+ }
+
+ private void initializeParserResult(String newLineSeparator) {
database = new BibDatabase();
+ database.setNewLineSeparator(newLineSeparator);
entryTypes = new HashSet<>(); // To store custom entry types parsed.
parserResult = new ParserResult(database, new MetaData(), entryTypes);
}
private void parseDatabaseID() throws IOException {
-
while (!eof) {
skipWhitespace();
char c = (char) read();
@@ -185,7 +211,7 @@ private ParserResult parseFileContent() throws IOException {
if ("preamble".equals(entryType)) {
database.setPreamble(parsePreamble());
- // Consume new line which signals end of preamble
+ // Consume a new line which separates the preamble from the next part (if the file was written with JabRef)
skipOneNewline();
// the preamble is saved verbatim anyways, so the text read so far can be dropped
dumpTextReadSoFarToString();
@@ -233,12 +259,23 @@ private void parseAndAddEntry(String type) {
// this is at least `@Type`
String commentsAndEntryTypeDefinition = dumpTextReadSoFarToString();
+ // remove first newline
+ // this is appended by JabRef during writing automatically
+ if (commentsAndEntryTypeDefinition.startsWith("\r\n")) {
+ commentsAndEntryTypeDefinition = commentsAndEntryTypeDefinition.substring(2);
+ } else if (commentsAndEntryTypeDefinition.startsWith("\n")) {
+ commentsAndEntryTypeDefinition = commentsAndEntryTypeDefinition.substring(1);
+ }
+
BibEntry entry = parseEntry(type);
// store comments collected without type definition
entry.setCommentsBeforeEntry(
commentsAndEntryTypeDefinition.substring(0, commentsAndEntryTypeDefinition.lastIndexOf('@')));
+
// store complete parsed serialization (comments, type definition + type contents)
- entry.setParsedSerialization(commentsAndEntryTypeDefinition + dumpTextReadSoFarToString());
+
+ String parsedSerialization = commentsAndEntryTypeDefinition + dumpTextReadSoFarToString();
+ entry.setParsedSerialization(parsedSerialization);
database.insertEntry(entry);
} catch (IOException ex) {
@@ -345,7 +382,13 @@ private String purge(String context, String stringToPurge) {
}
runningIndex++;
}
- return context.substring(runningIndex + 1);
+ // strip empty lines
+ while ((runningIndex < indexOfAt) &&
+ (context.charAt(runningIndex) == '\r' ||
+ context.charAt(runningIndex) == '\n')) {
+ runningIndex++;
+ }
+ return context.substring(runningIndex);
}
private String getPureTextFromFile() {
@@ -500,7 +543,10 @@ private BibtexString parseString() throws IOException {
private String parsePreamble() throws IOException {
skipWhitespace();
- return parseBracketedText();
+ String result = parseBracketedText();
+ // also "include" the newline in the preamble
+ skipOneNewline();
+ return result;
}
private BibEntry parseEntry(String entryType) throws IOException {
diff --git a/src/main/java/org/jabref/logic/importer/util/GroupsParser.java b/src/main/java/org/jabref/logic/importer/util/GroupsParser.java
index 0fc85c6b03a..da08aae3abc 100644
--- a/src/main/java/org/jabref/logic/importer/util/GroupsParser.java
+++ b/src/main/java/org/jabref/logic/importer/util/GroupsParser.java
@@ -135,7 +135,7 @@ private static AbstractGroup texGroupFromString(String string, FileUpdateMonitor
return newGroup;
} catch (IOException ex) {
// Problem accessing file -> create without file monitoring
- LOGGER.warn("Could not access file " + path + ". The group " + name + " will not reflect changes to the aux file.", ex);
+ LOGGER.warn("Could not access file {}. The group {} will not reflect changes to the aux file.", path, name, ex);
TexGroup newGroup = TexGroup.createWithoutFileMonitoring(name, context, path, new DefaultAuxParser(new BibDatabase()), fileMonitor, metaData);
addGroupDetails(tok, newGroup);
diff --git a/src/main/java/org/jabref/logic/importer/util/IdentifierParser.java b/src/main/java/org/jabref/logic/importer/util/IdentifierParser.java
index daa7a871e37..d5d86ad6a9a 100644
--- a/src/main/java/org/jabref/logic/importer/util/IdentifierParser.java
+++ b/src/main/java/org/jabref/logic/importer/util/IdentifierParser.java
@@ -5,8 +5,8 @@
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.StandardField;
+import org.jabref.model.entry.identifier.ArXivIdentifier;
import org.jabref.model.entry.identifier.DOI;
-import org.jabref.model.entry.identifier.Eprint;
import org.jabref.model.entry.identifier.ISBN;
import org.jabref.model.entry.identifier.Identifier;
import org.jabref.model.entry.identifier.MathSciNetId;
@@ -28,7 +28,7 @@ private static Function> getParserForFiel
} else if (StandardField.ISBN.equals(field)) {
return ISBN::parse;
} else if (StandardField.EPRINT.equals(field)) {
- return Eprint::build;
+ return ArXivIdentifier::parse;
} else if (StandardField.MR_NUMBER.equals(field)) {
return MathSciNetId::parse;
}
diff --git a/src/main/java/org/jabref/logic/l10n/Language.java b/src/main/java/org/jabref/logic/l10n/Language.java
index 0a6c3f90ee6..0711cd25d9e 100644
--- a/src/main/java/org/jabref/logic/l10n/Language.java
+++ b/src/main/java/org/jabref/logic/l10n/Language.java
@@ -30,7 +30,8 @@ public enum Language {
VIETNAMESE("Vietnamese", "vi"),
GREEK("ελληνικά", "el"),
TAGALOG("Tagalog/Filipino", "tl"),
- POLISH("Polish", "pl");
+ POLISH("Polish", "pl"),
+ KOREAN("Korean (한국어)", "ko");
private final String displayName;
private final String id;
diff --git a/src/main/java/org/jabref/logic/net/ProxyPreferences.java b/src/main/java/org/jabref/logic/net/ProxyPreferences.java
index fb63f11dfd2..5479e6beb72 100644
--- a/src/main/java/org/jabref/logic/net/ProxyPreferences.java
+++ b/src/main/java/org/jabref/logic/net/ProxyPreferences.java
@@ -2,105 +2,133 @@
import java.util.Objects;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
public class ProxyPreferences {
- private final Boolean useProxy;
- private final String hostname;
- private final String port;
- private final Boolean useAuthentication;
- private final String username;
- private final String password;
+ private final BooleanProperty useProxy;
+ private final StringProperty hostname;
+ private final StringProperty port;
+ private final BooleanProperty useAuthentication;
+ private final StringProperty username;
+ private final StringProperty password;
- public ProxyPreferences(Boolean useProxy, String hostname, String port, Boolean useAuthentication, String username,
+ public ProxyPreferences(Boolean useProxy,
+ String hostname,
+ String port,
+ Boolean useAuthentication,
+ String username,
String password) {
- this.useProxy = useProxy;
- this.hostname = hostname;
- this.port = port;
- this.useAuthentication = useAuthentication;
- this.username = username;
- this.password = password;
+ this.useProxy = new SimpleBooleanProperty(useProxy);
+ this.hostname = new SimpleStringProperty(hostname);
+ this.port = new SimpleStringProperty(port);
+ this.useAuthentication = new SimpleBooleanProperty(useAuthentication);
+ this.username = new SimpleStringProperty(username);
+ this.password = new SimpleStringProperty(password);
+ }
+
+ public final Boolean shouldUseProxy() {
+ return useProxy.getValue();
}
- public final Boolean isUseProxy() {
+ public BooleanProperty useProxyProperty() {
return useProxy;
}
+ public void setUseProxy(boolean useProxy) {
+ this.useProxy.set(useProxy);
+ }
+
public final String getHostname() {
+ return hostname.getValue();
+ }
+
+ public StringProperty hostnameProperty() {
return hostname;
}
+ public void setHostname(String hostname) {
+ this.hostname.set(hostname);
+ }
+
public final String getPort() {
+ return port.getValue();
+ }
+
+ public StringProperty portProperty() {
return port;
}
- public final Boolean isUseAuthentication() {
+ public void setPort(String port) {
+ this.port.set(port);
+ }
+
+ public final Boolean shouldUseAuthentication() {
+ return useAuthentication.getValue();
+ }
+
+ public BooleanProperty useAuthenticationProperty() {
return useAuthentication;
}
+ public void setUseAuthentication(boolean useAuthentication) {
+ this.useAuthentication.set(useAuthentication);
+ }
+
public final String getUsername() {
+ return username.getValue();
+ }
+
+ public StringProperty usernameProperty() {
return username;
}
+ public void setUsername(String username) {
+ this.username.set(username);
+ }
+
public final String getPassword() {
+ return password.getValue();
+ }
+
+ public StringProperty passwordProperty() {
return password;
}
- @Override
- public int hashCode() {
- return Objects.hash(hostname, password, port, useAuthentication, useProxy, username);
+ public void setPassword(String password) {
+ this.password.set(password);
}
@Override
- public boolean equals(Object obj) {
- if (this == obj) {
+ public boolean equals(Object o) {
+ if (this == o) {
return true;
}
- if (obj instanceof ProxyPreferences) {
- ProxyPreferences other = (ProxyPreferences) obj;
- if (hostname == null) {
- if (other.hostname != null) {
- return false;
- }
- } else if (!hostname.equals(other.hostname)) {
- return false;
- }
- if (password == null) {
- if (other.password != null) {
- return false;
- }
- } else if (!password.equals(other.password)) {
- return false;
- }
- if (port == null) {
- if (other.port != null) {
- return false;
- }
- } else if (!port.equals(other.port)) {
- return false;
- }
- if (useAuthentication == null) {
- if (other.useAuthentication != null) {
- return false;
- }
- } else if (!useAuthentication.equals(other.useAuthentication)) {
- return false;
- }
- if (useProxy == null) {
- if (other.useProxy != null) {
- return false;
- }
- } else if (!useProxy.equals(other.useProxy)) {
- return false;
- }
- if (username == null) {
- if (other.username != null) {
- return false;
- }
- } else if (!username.equals(other.username)) {
- return false;
- }
- return true;
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
}
- return false;
+
+ ProxyPreferences other = (ProxyPreferences) o;
+ return Objects.equals(useProxy.getValue(), other.useProxy.getValue())
+ && Objects.equals(hostname.getValue(), other.hostname.getValue())
+ && Objects.equals(port.getValue(), other.port.getValue())
+ && Objects.equals(useAuthentication.getValue(), other.useAuthentication.getValue())
+ && Objects.equals(username.getValue(), other.username.getValue())
+ && Objects.equals(password.getValue(), other.password.getValue());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ useProxy.getValue(),
+ hostname.getValue(),
+ port.getValue(),
+ useAuthentication.getValue(),
+ username.getValue(),
+ password.getValue());
}
}
diff --git a/src/main/java/org/jabref/logic/net/ProxyRegisterer.java b/src/main/java/org/jabref/logic/net/ProxyRegisterer.java
index 1b836552911..f5e8cc86d14 100644
--- a/src/main/java/org/jabref/logic/net/ProxyRegisterer.java
+++ b/src/main/java/org/jabref/logic/net/ProxyRegisterer.java
@@ -6,7 +6,7 @@ private ProxyRegisterer() {
}
public static void register(ProxyPreferences proxyPrefs) {
- if (proxyPrefs.isUseProxy()) {
+ if (proxyPrefs.shouldUseProxy()) {
// NetworkTabView.java ensures that proxyHostname and proxyPort are not null
System.setProperty("http.proxyHost", proxyPrefs.getHostname());
System.setProperty("http.proxyPort", proxyPrefs.getPort());
@@ -15,7 +15,7 @@ public static void register(ProxyPreferences proxyPrefs) {
System.setProperty("https.proxyPort", proxyPrefs.getPort());
// NetworkTabView.java ensures that proxyUsername and proxyPassword are neither null nor empty
- if (proxyPrefs.isUseAuthentication()) {
+ if (proxyPrefs.shouldUseAuthentication()) {
System.setProperty("http.proxyUser", proxyPrefs.getUsername());
System.setProperty("http.proxyPassword", proxyPrefs.getPassword());
diff --git a/src/main/java/org/jabref/logic/openoffice/OpenOfficePreferences.java b/src/main/java/org/jabref/logic/openoffice/OpenOfficePreferences.java
index 3efe5f3fd83..979114b62a7 100644
--- a/src/main/java/org/jabref/logic/openoffice/OpenOfficePreferences.java
+++ b/src/main/java/org/jabref/logic/openoffice/OpenOfficePreferences.java
@@ -1,6 +1,5 @@
package org.jabref.logic.openoffice;
-import java.util.ArrayList;
import java.util.List;
public class OpenOfficePreferences {
@@ -18,28 +17,23 @@ public class OpenOfficePreferences {
public static final String DEFAULT_LINUX_FLATPAK_EXEC_PATH = "/app/bin/soffice";
public static final String LINUX_EXECUTABLE = "soffice";
- private String executablePath = "";
- private String installationPath = "";
+ private String executablePath;
+ private String installationPath;
private boolean useAllDatabases;
private boolean syncWhenCiting;
- private boolean showPanel;
- private List externalStyles = new ArrayList<>();
- private String currentStyle = "";
-
- public OpenOfficePreferences(
- String executablePath,
- String installationPath,
- boolean useAllDatabases,
- boolean syncWhenCiting,
- boolean showPanel,
- List externalStyles,
- String currentStyle
- ) {
+ private List externalStyles;
+ private String currentStyle;
+
+ public OpenOfficePreferences(String executablePath,
+ String installationPath,
+ boolean useAllDatabases,
+ boolean syncWhenCiting,
+ List externalStyles,
+ String currentStyle) {
this.executablePath = executablePath;
this.installationPath = installationPath;
this.useAllDatabases = useAllDatabases;
this.syncWhenCiting = syncWhenCiting;
- this.showPanel = showPanel;
this.externalStyles = externalStyles;
this.currentStyle = currentStyle;
}
@@ -66,10 +60,6 @@ public String getInstallationPath() {
return installationPath;
}
- public void setInstallationPath(String installationPath) {
- this.installationPath = installationPath;
- }
-
/**
* true if all databases should be used when citing
*/
@@ -92,17 +82,6 @@ public void setSyncWhenCiting(boolean syncWhenCiting) {
this.syncWhenCiting = syncWhenCiting;
}
- /**
- * true if the OO panel is shown on startup
- */
- public boolean getShowPanel() {
- return showPanel;
- }
-
- public void setShowPanel(boolean showPanel) {
- this.showPanel = showPanel;
- }
-
/**
* list with paths to external style files
*/
@@ -125,11 +104,6 @@ public void setCurrentStyle(String currentStyle) {
this.currentStyle = currentStyle;
}
- public void updateConnectionParams(String ooPath, String execPath, String jarsPath) {
- setInstallationPath(ooPath);
- setExecutablePath(execPath);
- }
-
public void clearConnectionSettings() {
this.installationPath = "";
this.executablePath = "";
diff --git a/src/main/java/org/jabref/logic/openoffice/action/EditInsert.java b/src/main/java/org/jabref/logic/openoffice/action/EditInsert.java
new file mode 100644
index 00000000000..0004d3fe6db
--- /dev/null
+++ b/src/main/java/org/jabref/logic/openoffice/action/EditInsert.java
@@ -0,0 +1,118 @@
+package org.jabref.logic.openoffice.action;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import org.jabref.logic.openoffice.frontend.OOFrontend;
+import org.jabref.logic.openoffice.frontend.UpdateCitationMarkers;
+import org.jabref.logic.openoffice.style.OOBibStyle;
+import org.jabref.model.database.BibDatabase;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.openoffice.ootext.OOText;
+import org.jabref.model.openoffice.style.Citation;
+import org.jabref.model.openoffice.style.CitationMarkerEntry;
+import org.jabref.model.openoffice.style.CitationType;
+import org.jabref.model.openoffice.style.NonUniqueCitationMarker;
+import org.jabref.model.openoffice.style.OODataModel;
+import org.jabref.model.openoffice.uno.CreationException;
+import org.jabref.model.openoffice.uno.NoDocumentException;
+import org.jabref.model.openoffice.uno.UnoScreenRefresh;
+import org.jabref.model.openoffice.util.OOListUtil;
+import org.jabref.model.strings.StringUtil;
+
+import com.sun.star.beans.IllegalTypeException;
+import com.sun.star.beans.NotRemoveableException;
+import com.sun.star.beans.PropertyVetoException;
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.text.XTextCursor;
+import com.sun.star.text.XTextDocument;
+
+public class EditInsert {
+
+ private EditInsert() {
+ /**/
+ }
+
+ /**
+ * In insertEntry we receive BibEntry values from the GUI.
+ *
+ * In the document we store citations by their citation key.
+ *
+ * If the citation key is missing, the best we can do is to notify the user. Or the programmer,
+ * that we cannot accept such input.
+ *
+ */
+ private static String insertEntryGetCitationKey(BibEntry entry) {
+ Optional key = entry.getCitationKey();
+ if (key.isEmpty()) {
+ throw new IllegalArgumentException("insertEntryGetCitationKey: cannot cite entries without citation key");
+ }
+ return key.get();
+ }
+
+ /**
+ * @param cursor Where to insert.
+ * @param pageInfo A single pageInfo for a list of entries. This is what we get from the GUI.
+ */
+ public static void insertCitationGroup(XTextDocument doc,
+ OOFrontend frontend,
+ XTextCursor cursor,
+ List entries,
+ BibDatabase database,
+ OOBibStyle style,
+ CitationType citationType,
+ String pageInfo)
+ throws
+ NoDocumentException,
+ NotRemoveableException,
+ WrappedTargetException,
+ PropertyVetoException,
+ CreationException,
+ IllegalTypeException {
+
+ List citationKeys = OOListUtil.map(entries, EditInsert::insertEntryGetCitationKey);
+
+ final int totalEntries = entries.size();
+ List> pageInfos = OODataModel.fakePageInfos(pageInfo, totalEntries);
+
+ List citations = new ArrayList<>(totalEntries);
+ for (int i = 0; i < totalEntries; i++) {
+ Citation cit = new Citation(citationKeys.get(i));
+ cit.lookupInDatabases(Collections.singletonList(database));
+ cit.setPageInfo(pageInfos.get(i));
+ citations.add(cit);
+ }
+
+ // The text we insert
+ OOText citeText = null;
+ if (style.isNumberEntries()) {
+ citeText = OOText.fromString("[-]"); // A dash only. Only refresh later.
+ } else {
+ citeText = style.createCitationMarker(citations,
+ citationType.inParenthesis(),
+ NonUniqueCitationMarker.FORGIVEN);
+ }
+
+ if (StringUtil.isBlank(OOText.toString(citeText))) {
+ citeText = OOText.fromString("[?]");
+ }
+
+ try {
+ UnoScreenRefresh.lockControllers(doc);
+ UpdateCitationMarkers.createAndFillCitationGroup(frontend,
+ doc,
+ citationKeys,
+ pageInfos,
+ citationType,
+ citeText,
+ cursor,
+ style,
+ true /* insertSpaceAfter */);
+ } finally {
+ UnoScreenRefresh.unlockControllers(doc);
+ }
+
+ }
+}
diff --git a/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java b/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java
new file mode 100644
index 00000000000..f91e8aec93d
--- /dev/null
+++ b/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java
@@ -0,0 +1,344 @@
+package org.jabref.logic.openoffice.action;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.jabref.logic.openoffice.frontend.OOFrontend;
+import org.jabref.logic.openoffice.frontend.UpdateCitationMarkers;
+import org.jabref.logic.openoffice.style.OOBibStyle;
+import org.jabref.model.openoffice.ootext.OOText;
+import org.jabref.model.openoffice.style.Citation;
+import org.jabref.model.openoffice.style.CitationGroup;
+import org.jabref.model.openoffice.style.CitationType;
+import org.jabref.model.openoffice.uno.CreationException;
+import org.jabref.model.openoffice.uno.NoDocumentException;
+import org.jabref.model.openoffice.uno.UnoScreenRefresh;
+import org.jabref.model.openoffice.uno.UnoTextRange;
+import org.jabref.model.openoffice.util.OOListUtil;
+
+import com.sun.star.beans.IllegalTypeException;
+import com.sun.star.beans.NotRemoveableException;
+import com.sun.star.beans.PropertyVetoException;
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.text.XTextCursor;
+import com.sun.star.text.XTextDocument;
+import com.sun.star.text.XTextRange;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EditMerge {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EditMerge.class);
+
+ private EditMerge() {
+ // hide constructor
+ }
+
+ /*
+ * @return true if modified document
+ */
+ public static boolean mergeCitationGroups(XTextDocument doc, OOFrontend frontend, OOBibStyle style)
+ throws
+ CreationException,
+ IllegalArgumentException,
+ IllegalTypeException,
+ NoDocumentException,
+ NotRemoveableException,
+ PropertyVetoException,
+ WrappedTargetException {
+
+ boolean madeModifications;
+
+ try {
+ UnoScreenRefresh.lockControllers(doc);
+
+ List joinableGroups = EditMerge.scan(doc, frontend);
+
+ for (JoinableGroupData joinableGroupData : joinableGroups) {
+
+ List groups = joinableGroupData.group;
+
+ List newCitations = (groups.stream()
+ .flatMap(group -> group.citationsInStorageOrder.stream())
+ .collect(Collectors.toList()));
+
+ CitationType citationType = groups.get(0).citationType;
+ List> pageInfos = frontend.backend.combinePageInfos(groups);
+
+ frontend.removeCitationGroups(groups, doc);
+ XTextCursor textCursor = joinableGroupData.groupCursor;
+ textCursor.setString(""); // Also remove the spaces between.
+
+ List citationKeys = OOListUtil.map(newCitations, Citation::getCitationKey);
+
+ /* insertSpaceAfter: no, it is already there (or could be) */
+ boolean insertSpaceAfter = false;
+ UpdateCitationMarkers.createAndFillCitationGroup(frontend,
+ doc,
+ citationKeys,
+ pageInfos,
+ citationType,
+ OOText.fromString("tmp"),
+ textCursor,
+ style,
+ insertSpaceAfter);
+ }
+
+ madeModifications = !joinableGroups.isEmpty();
+
+ } finally {
+ UnoScreenRefresh.unlockControllers(doc);
+ }
+
+ return madeModifications;
+ }
+
+ private static class JoinableGroupData {
+ /** A list of consecutive citation groups only separated by spaces. */
+ List group;
+
+ /** A cursor covering the XTextRange of each entry in group (and the spaces between them) */
+ XTextCursor groupCursor;
+
+ JoinableGroupData(List group, XTextCursor groupCursor) {
+ this.group = group;
+ this.groupCursor = groupCursor;
+ }
+ }
+
+ private static class ScanState {
+
+ // Citation groups in the current group
+ List currentGroup;
+
+ // A cursor that covers the Citation groups in currentGroup, including the space between
+ // them.
+ // null if currentGroup.isEmpty()
+ XTextCursor currentGroupCursor;
+
+ // A cursor starting at the end of the last CitationGroup in
+ // currentGroup. null if currentGroup.isEmpty()
+ XTextCursor cursorBetween;
+
+ // The last element of currentGroup.
+ // null if currentGroup.isEmpty()
+ CitationGroup prev;
+
+ // The XTextRange for prev.
+ // null if currentGroup.isEmpty()
+ XTextRange prevRange;
+
+ ScanState() {
+ reset();
+ }
+
+ void reset() {
+ currentGroup = new ArrayList<>();
+ currentGroupCursor = null;
+ cursorBetween = null;
+ prev = null;
+ prevRange = null;
+ }
+ }
+
+ /**
+ * Decide if group could be added to state.currentGroup
+ *
+ * @param group The CitationGroup to test
+ * @param currentRange The XTextRange corresponding to group.
+ *
+ * @return false if cannot add, true if can. If returned true, then state.cursorBetween and
+ * state.currentGroupCursor are expanded to end at the start of currentRange.
+ */
+ private static boolean checkAddToGroup(ScanState state, CitationGroup group, XTextRange currentRange) {
+
+ if (state.currentGroup.isEmpty()) {
+ return false;
+ }
+
+ Objects.requireNonNull(state.currentGroupCursor);
+ Objects.requireNonNull(state.cursorBetween);
+ Objects.requireNonNull(state.prev);
+ Objects.requireNonNull(state.prevRange);
+
+ // Only combine (Author 2000) type citations
+ if (group.citationType != CitationType.AUTHORYEAR_PAR) {
+ return false;
+ }
+
+ if (state.prev != null) {
+
+ // Even if we combine AUTHORYEAR_INTEXT citations, we would not mix them with AUTHORYEAR_PAR
+ if (group.citationType != state.prev.citationType) {
+ return false;
+ }
+
+ if (!UnoTextRange.comparables(state.prevRange, currentRange)) {
+ return false;
+ }
+
+ // Sanity check: the current range should start later than the previous.
+ int textOrder = UnoTextRange.compareStarts(state.prevRange, currentRange);
+ if (textOrder != (-1)) {
+ String msg =
+ String.format("MergeCitationGroups:"
+ + " \"%s\" supposed to be followed by \"%s\","
+ + " but %s",
+ state.prevRange.getString(),
+ currentRange.getString(),
+ ((textOrder == 0)
+ ? "they start at the same position"
+ : "the start of the latter precedes the start of the first"));
+ LOGGER.warn(msg);
+ return false;
+ }
+ }
+
+ if (state.cursorBetween == null) {
+ return false;
+ }
+
+ Objects.requireNonNull(state.cursorBetween);
+ Objects.requireNonNull(state.currentGroupCursor);
+
+ // assume: currentGroupCursor.getEnd() == cursorBetween.getEnd()
+ if (UnoTextRange.compareEnds(state.cursorBetween, state.currentGroupCursor) != 0) {
+ LOGGER.warn("MergeCitationGroups: cursorBetween.end != currentGroupCursor.end");
+ throw new IllegalStateException("MergeCitationGroups failed");
+ }
+
+ /*
+ * Try to expand state.currentGroupCursor and state.cursorBetween by going right to reach
+ * rangeStart.
+ */
+ XTextRange rangeStart = currentRange.getStart();
+ boolean couldExpand = true;
+ XTextCursor thisCharCursor =
+ (currentRange.getText().createTextCursorByRange(state.cursorBetween.getEnd()));
+
+ while (couldExpand && (UnoTextRange.compareEnds(state.cursorBetween, rangeStart) < 0)) {
+ //
+ // Check that we only walk through inline whitespace.
+ //
+ couldExpand = thisCharCursor.goRight((short) 1, true);
+ String thisChar = thisCharCursor.getString();
+ thisCharCursor.collapseToEnd();
+ if (thisChar.isEmpty() || "\n".equals(thisChar) || !thisChar.trim().isEmpty()) {
+ couldExpand = false;
+ if (!thisChar.isEmpty()) {
+ thisCharCursor.goLeft((short) 1, false);
+ }
+ break;
+ }
+ state.cursorBetween.goRight((short) 1, true);
+ state.currentGroupCursor.goRight((short) 1, true);
+
+ // These two should move in sync:
+ if (UnoTextRange.compareEnds(state.cursorBetween, state.currentGroupCursor) != 0) {
+ LOGGER.warn("MergeCitationGroups: cursorBetween.end != currentGroupCursor.end (during expand)");
+ throw new IllegalStateException("MergeCitationGroups failed");
+ }
+ }
+
+ return couldExpand;
+ }
+
+ /**
+ * Add group to state.currentGroup
+ * Set state.cursorBetween to start at currentRange.getEnd()
+ * Expand state.currentGroupCursor to also cover currentRange
+ * Set state.prev to group, state.prevRange to currentRange
+ */
+ private static void addToCurrentGroup(ScanState state, CitationGroup group, XTextRange currentRange) {
+ final boolean isNewGroup = state.currentGroup.isEmpty();
+ if (!isNewGroup) {
+ Objects.requireNonNull(state.currentGroupCursor);
+ Objects.requireNonNull(state.cursorBetween);
+ Objects.requireNonNull(state.prev);
+ Objects.requireNonNull(state.prevRange);
+ }
+
+ // Add the current entry to a group.
+ state.currentGroup.add(group);
+
+ // Set up cursorBetween to start at currentRange.getEnd()
+ XTextRange rangeEnd = currentRange.getEnd();
+ state.cursorBetween = currentRange.getText().createTextCursorByRange(rangeEnd);
+
+ // If new group, create currentGroupCursor
+ if (isNewGroup) {
+ state.currentGroupCursor = (currentRange.getText()
+ .createTextCursorByRange(currentRange.getStart()));
+ }
+
+ // include currentRange in currentGroupCursor
+ state.currentGroupCursor.goRight((short) (currentRange.getString().length()), true);
+
+ if (UnoTextRange.compareEnds(state.cursorBetween, state.currentGroupCursor) != 0) {
+ LOGGER.warn("MergeCitationGroups: cursorBetween.end != currentGroupCursor.end");
+ throw new IllegalStateException("MergeCitationGroups failed");
+ }
+
+ /* Store data about last entry in currentGroup */
+ state.prev = group;
+ state.prevRange = currentRange;
+ }
+
+ /**
+ * Scan the document for joinable groups. Return those found.
+ */
+ private static List scan(XTextDocument doc, OOFrontend frontend)
+ throws
+ NoDocumentException,
+ WrappedTargetException {
+ List result = new ArrayList<>();
+
+ List groups = frontend.getCitationGroupsSortedWithinPartitions(doc, false /* mapFootnotesToFootnoteMarks */);
+ if (groups.isEmpty()) {
+ return result;
+ }
+
+ ScanState state = new ScanState();
+
+ for (CitationGroup group : groups) {
+
+ XTextRange currentRange = (frontend.getMarkRange(doc, group)
+ .orElseThrow(IllegalStateException::new));
+
+ /*
+ * Decide if we add group to the group. False when the group is empty.
+ */
+ boolean addToGroup = checkAddToGroup(state, group, currentRange);
+
+ /*
+ * Even if we do not add it to an existing group, we might use it to start a new group.
+ *
+ * Can it start a new group?
+ */
+ boolean canStartGroup = (group.citationType == CitationType.AUTHORYEAR_PAR);
+
+ if (!addToGroup) {
+ // close currentGroup
+ if (state.currentGroup.size() > 1) {
+ result.add(new JoinableGroupData(state.currentGroup, state.currentGroupCursor));
+ }
+ // Start a new, empty group
+ state.reset();
+ }
+
+ if (addToGroup || canStartGroup) {
+ addToCurrentGroup(state, group, currentRange);
+ }
+ }
+
+ // close currentGroup
+ if (state.currentGroup.size() > 1) {
+ result.add(new JoinableGroupData(state.currentGroup, state.currentGroupCursor));
+ }
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/jabref/logic/openoffice/action/EditSeparate.java b/src/main/java/org/jabref/logic/openoffice/action/EditSeparate.java
new file mode 100644
index 00000000000..66630a6f8d0
--- /dev/null
+++ b/src/main/java/org/jabref/logic/openoffice/action/EditSeparate.java
@@ -0,0 +1,99 @@
+package org.jabref.logic.openoffice.action;
+
+import java.util.List;
+
+import org.jabref.logic.openoffice.frontend.OOFrontend;
+import org.jabref.logic.openoffice.frontend.UpdateCitationMarkers;
+import org.jabref.logic.openoffice.style.OOBibStyle;
+import org.jabref.logic.openoffice.style.OOProcess;
+import org.jabref.model.database.BibDatabase;
+import org.jabref.model.openoffice.ootext.OOText;
+import org.jabref.model.openoffice.style.Citation;
+import org.jabref.model.openoffice.style.CitationGroup;
+import org.jabref.model.openoffice.uno.CreationException;
+import org.jabref.model.openoffice.uno.NoDocumentException;
+import org.jabref.model.openoffice.uno.UnoScreenRefresh;
+
+import com.sun.star.beans.IllegalTypeException;
+import com.sun.star.beans.NotRemoveableException;
+import com.sun.star.beans.PropertyVetoException;
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.text.XTextCursor;
+import com.sun.star.text.XTextDocument;
+import com.sun.star.text.XTextRange;
+
+public class EditSeparate {
+
+ private EditSeparate() {
+ /**/
+ }
+
+ public static boolean separateCitations(XTextDocument doc,
+ OOFrontend frontend,
+ List databases,
+ OOBibStyle style)
+ throws
+ CreationException,
+ IllegalTypeException,
+ NoDocumentException,
+ NotRemoveableException,
+ PropertyVetoException,
+ WrappedTargetException,
+ com.sun.star.lang.IllegalArgumentException {
+
+ boolean madeModifications = false;
+
+ // To reduce surprises in JabRef52 mode, impose localOrder to
+ // decide the visually last Citation in the group. Unless the
+ // style changed since refresh this is the last on the screen
+ // as well.
+ frontend.citationGroups.lookupCitations(databases);
+ frontend.citationGroups.imposeLocalOrder(OOProcess.comparatorForMulticite(style));
+
+ List groups = frontend.citationGroups.getCitationGroupsUnordered();
+
+ try {
+ UnoScreenRefresh.lockControllers(doc);
+
+ for (CitationGroup group : groups) {
+
+ XTextRange range1 = (frontend
+ .getMarkRange(doc, group)
+ .orElseThrow(IllegalStateException::new));
+ XTextCursor textCursor = range1.getText().createTextCursorByRange(range1);
+
+ List citations = group.citationsInStorageOrder;
+ if (citations.size() <= 1) {
+ continue;
+ }
+
+ frontend.removeCitationGroup(group, doc);
+ // Now we own the content of citations
+
+ // Create a citation group for each citation.
+ final int last = citations.size() - 1;
+ for (int i = 0; i < citations.size(); i++) {
+ boolean insertSpaceAfter = (i != last);
+ Citation citation = citations.get(i);
+
+ UpdateCitationMarkers.createAndFillCitationGroup(frontend,
+ doc,
+ List.of(citation.citationKey),
+ List.of(citation.getPageInfo()),
+ group.citationType,
+ OOText.fromString(citation.citationKey),
+ textCursor,
+ style,
+ insertSpaceAfter);
+
+ textCursor.collapseToEnd();
+ }
+
+ madeModifications = true;
+ }
+ } finally {
+ UnoScreenRefresh.unlockControllers(doc);
+ }
+ return madeModifications;
+ }
+}
diff --git a/src/main/java/org/jabref/logic/openoffice/action/ExportCited.java b/src/main/java/org/jabref/logic/openoffice/action/ExportCited.java
new file mode 100644
index 00000000000..c38cd4fd4cc
--- /dev/null
+++ b/src/main/java/org/jabref/logic/openoffice/action/ExportCited.java
@@ -0,0 +1,98 @@
+package org.jabref.logic.openoffice.action;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.jabref.logic.openoffice.frontend.OOFrontend;
+import org.jabref.model.database.BibDatabase;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.field.StandardField;
+import org.jabref.model.openoffice.style.CitedKey;
+import org.jabref.model.openoffice.style.CitedKeys;
+import org.jabref.model.openoffice.uno.NoDocumentException;
+
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.text.XTextDocument;
+
+public class ExportCited {
+
+ private ExportCited() {
+ /**/
+ }
+
+ public static class GenerateDatabaseResult {
+ /**
+ * null: not done; isEmpty: no unresolved
+ */
+ public final List unresolvedKeys;
+ public final BibDatabase newDatabase;
+
+ GenerateDatabaseResult(List unresolvedKeys, BibDatabase newDatabase) {
+ this.unresolvedKeys = unresolvedKeys;
+ this.newDatabase = newDatabase;
+ }
+ }
+
+ /**
+ *
+ * @param databases The databases to look up the citation keys in the document from.
+ * @return A new database, with cloned entries.
+ *
+ * If a key is not found, it is added to result.unresolvedKeys
+ *
+ * Cross references (in StandardField.CROSSREF) are followed (not recursively):
+ * If the referenced entry is found, it is included in the result.
+ * If it is not found, it is silently ignored.
+ */
+ public static GenerateDatabaseResult generateDatabase(XTextDocument doc, List databases)
+ throws
+ NoDocumentException,
+ WrappedTargetException {
+
+ OOFrontend frontend = new OOFrontend(doc);
+ CitedKeys citationKeys = frontend.citationGroups.getCitedKeysUnordered();
+ citationKeys.lookupInDatabases(databases);
+
+ List unresolvedKeys = new ArrayList<>();
+ BibDatabase resultDatabase = new BibDatabase();
+
+ List entriesToInsert = new ArrayList<>();
+ Set seen = new HashSet<>(); // Only add crossReference once.
+
+ for (CitedKey citation : citationKeys.values()) {
+ if (citation.getLookupResult().isEmpty()) {
+ unresolvedKeys.add(citation.citationKey);
+ continue;
+ } else {
+ BibEntry entry = citation.getLookupResult().get().entry;
+ BibDatabase loopDatabase = citation.getLookupResult().get().database;
+
+ // If entry found
+ BibEntry clonedEntry = (BibEntry) entry.clone();
+
+ // Insert a copy of the entry
+ entriesToInsert.add(clonedEntry);
+
+ // Check if the cloned entry has a cross-reference field
+ clonedEntry
+ .getField(StandardField.CROSSREF)
+ .ifPresent(crossReference -> {
+ boolean isNew = !seen.contains(crossReference);
+ if (isNew) {
+ // Add it if it is in the current library
+ loopDatabase
+ .getEntryByCitationKey(crossReference)
+ .ifPresent(entriesToInsert::add);
+ seen.add(crossReference);
+ }
+ });
+ }
+ }
+
+ resultDatabase.insertEntries(entriesToInsert);
+ return new GenerateDatabaseResult(unresolvedKeys, resultDatabase);
+ }
+
+}
diff --git a/src/main/java/org/jabref/logic/openoffice/action/ManageCitations.java b/src/main/java/org/jabref/logic/openoffice/action/ManageCitations.java
new file mode 100644
index 00000000000..c8234df9ca0
--- /dev/null
+++ b/src/main/java/org/jabref/logic/openoffice/action/ManageCitations.java
@@ -0,0 +1,38 @@
+package org.jabref.logic.openoffice.action;
+
+import java.util.List;
+
+import org.jabref.logic.openoffice.frontend.OOFrontend;
+import org.jabref.model.openoffice.CitationEntry;
+import org.jabref.model.openoffice.uno.NoDocumentException;
+
+import com.sun.star.beans.IllegalTypeException;
+import com.sun.star.beans.PropertyVetoException;
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.text.XTextDocument;
+
+public class ManageCitations {
+
+ private ManageCitations() {
+ /**/
+ }
+
+ public static List getCitationEntries(XTextDocument doc)
+ throws
+ NoDocumentException,
+ WrappedTargetException {
+ OOFrontend frontend = new OOFrontend(doc);
+ return frontend.getCitationEntries(doc);
+ }
+
+ public static void applyCitationEntries(XTextDocument doc, List citationEntries)
+ throws
+ NoDocumentException,
+ PropertyVetoException,
+ IllegalTypeException,
+ WrappedTargetException,
+ IllegalArgumentException {
+ OOFrontend frontend = new OOFrontend(doc);
+ frontend.applyCitationEntries(doc, citationEntries);
+ }
+}
diff --git a/src/main/java/org/jabref/logic/openoffice/action/Update.java b/src/main/java/org/jabref/logic/openoffice/action/Update.java
new file mode 100644
index 00000000000..80263833aec
--- /dev/null
+++ b/src/main/java/org/jabref/logic/openoffice/action/Update.java
@@ -0,0 +1,133 @@
+package org.jabref.logic.openoffice.action;
+
+import java.util.List;
+
+import org.jabref.logic.openoffice.frontend.OOFrontend;
+import org.jabref.logic.openoffice.frontend.UpdateBibliography;
+import org.jabref.logic.openoffice.frontend.UpdateCitationMarkers;
+import org.jabref.logic.openoffice.style.OOBibStyle;
+import org.jabref.logic.openoffice.style.OOProcess;
+import org.jabref.model.database.BibDatabase;
+import org.jabref.model.openoffice.rangesort.FunctionalTextViewCursor;
+import org.jabref.model.openoffice.uno.CreationException;
+import org.jabref.model.openoffice.uno.NoDocumentException;
+import org.jabref.model.openoffice.uno.UnoScreenRefresh;
+
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.text.XTextDocument;
+
+/**
+ * Update document: citation marks and bibliography
+ */
+public class Update {
+
+ static final boolean USE_LOCK_CONTROLLERS = true;
+
+ private Update() {
+ /**/
+ }
+
+ /**
+ * @return the list of unresolved citation keys
+ */
+ private static List updateDocument(XTextDocument doc,
+ OOFrontend frontend,
+ List databases,
+ OOBibStyle style,
+ FunctionalTextViewCursor fcursor,
+ boolean doUpdateBibliography,
+ boolean alwaysAddCitedOnPages)
+ throws
+ CreationException,
+ NoDocumentException,
+ WrappedTargetException,
+ com.sun.star.lang.IllegalArgumentException {
+
+ frontend.imposeGlobalOrder(doc, fcursor);
+ OOProcess.produceCitationMarkers(frontend.citationGroups, databases, style);
+
+ try {
+ if (USE_LOCK_CONTROLLERS) {
+ UnoScreenRefresh.lockControllers(doc);
+ }
+
+ UpdateCitationMarkers.applyNewCitationMarkers(doc, frontend, style);
+
+ if (doUpdateBibliography) {
+ UpdateBibliography.rebuildBibTextSection(doc,
+ frontend,
+ frontend.citationGroups.getBibliography().get(),
+ style,
+ alwaysAddCitedOnPages);
+ }
+
+ return frontend.citationGroups.getUnresolvedKeys();
+ } finally {
+ if (USE_LOCK_CONTROLLERS && UnoScreenRefresh.hasControllersLocked(doc)) {
+ UnoScreenRefresh.unlockControllers(doc);
+ }
+ }
+ }
+
+ public static class SyncOptions {
+
+ public final List databases;
+ boolean updateBibliography;
+ boolean alwaysAddCitedOnPages;
+
+ public SyncOptions(List databases) {
+ this.databases = databases;
+ this.updateBibliography = false;
+ this.alwaysAddCitedOnPages = false;
+ }
+
+ public SyncOptions setUpdateBibliography(boolean value) {
+ this.updateBibliography = value;
+ return this;
+ }
+
+ public SyncOptions setAlwaysAddCitedOnPages(boolean value) {
+ this.alwaysAddCitedOnPages = value;
+ return this;
+ }
+ }
+
+ public static List synchronizeDocument(XTextDocument doc,
+ OOFrontend frontend,
+ OOBibStyle style,
+ FunctionalTextViewCursor fcursor,
+ SyncOptions syncOptions)
+ throws
+ CreationException,
+ NoDocumentException,
+ WrappedTargetException,
+ com.sun.star.lang.IllegalArgumentException {
+
+ return Update.updateDocument(doc,
+ frontend,
+ syncOptions.databases,
+ style,
+ fcursor,
+ syncOptions.updateBibliography,
+ syncOptions.alwaysAddCitedOnPages);
+ }
+
+ /*
+ * Reread document before sync
+ */
+ public static List resyncDocument(XTextDocument doc,
+ OOBibStyle style,
+ FunctionalTextViewCursor fcursor,
+ SyncOptions syncOptions)
+ throws
+ CreationException,
+ NoDocumentException,
+ WrappedTargetException,
+ com.sun.star.lang.IllegalArgumentException {
+
+ OOFrontend frontend = new OOFrontend(doc);
+
+ return Update.synchronizeDocument(doc, frontend, style, fcursor, syncOptions);
+ }
+
+}
diff --git a/src/main/java/org/jabref/logic/remote/RemotePreferences.java b/src/main/java/org/jabref/logic/remote/RemotePreferences.java
index 67a57f3621e..901bce68f53 100644
--- a/src/main/java/org/jabref/logic/remote/RemotePreferences.java
+++ b/src/main/java/org/jabref/logic/remote/RemotePreferences.java
@@ -3,33 +3,46 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+
/**
* Place for handling the preferences for the remote communication
*/
public class RemotePreferences {
- private int port;
- private boolean useRemoteServer;
+ private IntegerProperty port;
+ private BooleanProperty useRemoteServer;
public RemotePreferences(int port, boolean useRemoteServer) {
- this.port = port;
- this.useRemoteServer = useRemoteServer;
+ this.port = new SimpleIntegerProperty(port);
+ this.useRemoteServer = new SimpleBooleanProperty(useRemoteServer);
}
public int getPort() {
+ return port.getValue();
+ }
+
+ public IntegerProperty portProperty() {
return port;
}
public void setPort(int port) {
- this.port = port;
+ this.port.setValue(port);
}
public boolean useRemoteServer() {
+ return useRemoteServer.getValue();
+ }
+
+ public BooleanProperty useRemoteServerProperty() {
return useRemoteServer;
}
public void setUseRemoteServer(boolean useRemoteServer) {
- this.useRemoteServer = useRemoteServer;
+ this.useRemoteServer.setValue(useRemoteServer);
}
public boolean isDifferentPort(int otherPort) {
diff --git a/src/main/java/org/jabref/logic/texparser/TexBibEntriesResolver.java b/src/main/java/org/jabref/logic/texparser/TexBibEntriesResolver.java
index 49e1fd25931..84322da6ece 100644
--- a/src/main/java/org/jabref/logic/texparser/TexBibEntriesResolver.java
+++ b/src/main/java/org/jabref/logic/texparser/TexBibEntriesResolver.java
@@ -17,6 +17,7 @@
import org.jabref.model.texparser.LatexBibEntriesResolverResult;
import org.jabref.model.texparser.LatexParserResult;
import org.jabref.model.util.FileUpdateMonitor;
+import org.jabref.preferences.GeneralPreferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,11 +27,13 @@ public class TexBibEntriesResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(TexBibEntriesResolver.class);
private final BibDatabase masterDatabase;
+ private final GeneralPreferences generalPreferences;
private final ImportFormatPreferences importFormatPreferences;
private final FileUpdateMonitor fileMonitor;
- public TexBibEntriesResolver(BibDatabase masterDatabase, ImportFormatPreferences importFormatPreferences, FileUpdateMonitor fileMonitor) {
+ public TexBibEntriesResolver(BibDatabase masterDatabase, GeneralPreferences generalPreferences, ImportFormatPreferences importFormatPreferences, FileUpdateMonitor fileMonitor) {
this.masterDatabase = masterDatabase;
+ this.generalPreferences = generalPreferences;
this.importFormatPreferences = importFormatPreferences;
this.fileMonitor = fileMonitor;
}
@@ -45,7 +48,7 @@ public LatexBibEntriesResolverResult resolve(LatexParserResult latexParserResult
Map bibDatabases = resolverResult.getBibFiles().values().stream().distinct().collect(Collectors.toMap(
Function.identity(), path -> {
try {
- return OpenDatabase.loadDatabase(path, importFormatPreferences, fileMonitor).getDatabase();
+ return OpenDatabase.loadDatabase(path, generalPreferences, importFormatPreferences, fileMonitor).getDatabase();
} catch (IOException e) {
LOGGER.error("Error opening file '{}'", path, e);
return ParserResult.fromError(e).getDatabase();
diff --git a/src/main/java/org/jabref/logic/util/OS.java b/src/main/java/org/jabref/logic/util/OS.java
index 2f97a16de20..d56a8a41363 100644
--- a/src/main/java/org/jabref/logic/util/OS.java
+++ b/src/main/java/org/jabref/logic/util/OS.java
@@ -6,12 +6,10 @@
* Operating system (OS) detection
*/
public class OS {
- // File separator obtained from system
- public static final String FILE_SEPARATOR = System.getProperty("file.separator");
+ public static final String NEWLINE = System.lineSeparator();
- // Newlines
- // will be overridden in initialization due to feature #857 @ JabRef.java
- public static String NEWLINE = System.lineSeparator();
+ // File separator obtained from system
+ private static final String FILE_SEPARATOR = System.getProperty("file.separator");
// https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/SystemUtils.html
private static final String OS_NAME = System.getProperty("os.name", "unknown").toLowerCase(Locale.ROOT);
diff --git a/src/main/java/org/jabref/logic/util/Version.java b/src/main/java/org/jabref/logic/util/Version.java
index 866326068aa..567fdbb8403 100644
--- a/src/main/java/org/jabref/logic/util/Version.java
+++ b/src/main/java/org/jabref/logic/util/Version.java
@@ -12,8 +12,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.jabref.logic.net.URLDownload;
-
import kong.unirest.json.JSONArray;
import kong.unirest.json.JSONObject;
import org.slf4j.Logger;
@@ -96,7 +94,6 @@ public static Version parse(String version) {
* Grabs all the available releases from the GitHub repository
*/
public static List getAllAvailableVersions() throws IOException {
- URLDownload.bypassSSLVerification();
HttpURLConnection connection = (HttpURLConnection) new URL(JABREF_GITHUB_RELEASES).openConnection();
connection.setRequestProperty("Accept-Charset", "UTF-8");
try (BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
diff --git a/src/main/java/org/jabref/logic/util/io/AutoLinkPreferences.java b/src/main/java/org/jabref/logic/util/io/AutoLinkPreferences.java
index 58cb5d96972..09db96944c3 100644
--- a/src/main/java/org/jabref/logic/util/io/AutoLinkPreferences.java
+++ b/src/main/java/org/jabref/logic/util/io/AutoLinkPreferences.java
@@ -1,5 +1,12 @@
package org.jabref.logic.util.io;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
public class AutoLinkPreferences {
public enum CitationKeyDependency {
@@ -8,39 +15,66 @@ public enum CitationKeyDependency {
REGEX // Filenames matching a regular expression pattern
}
- private final CitationKeyDependency citationKeyDependency;
- private final String regularExpression;
- private boolean askAutoNamingPdfs;
- private final Character keywordDelimiter;
+ private final ObjectProperty citationKeyDependency;
+ private final StringProperty regularExpression;
+ private final BooleanProperty askAutoNamingPdfs;
+ private final SimpleObjectProperty keywordDelimiter;
public AutoLinkPreferences(CitationKeyDependency citationKeyDependency,
String regularExpression,
boolean askAutoNamingPdfs,
Character keywordDelimiter) {
- this.citationKeyDependency = citationKeyDependency;
- this.regularExpression = regularExpression;
- this.askAutoNamingPdfs = askAutoNamingPdfs;
- this.keywordDelimiter = keywordDelimiter;
+ this.citationKeyDependency = new SimpleObjectProperty<>(citationKeyDependency);
+ this.regularExpression = new SimpleStringProperty(regularExpression);
+ this.askAutoNamingPdfs = new SimpleBooleanProperty(askAutoNamingPdfs);
+ this.keywordDelimiter = new SimpleObjectProperty<>(keywordDelimiter);
}
public CitationKeyDependency getCitationKeyDependency() {
+ return citationKeyDependency.getValue();
+ }
+
+ public ObjectProperty citationKeyDependencyProperty() {
return citationKeyDependency;
}
+ public void setCitationKeyDependency(CitationKeyDependency citationKeyDependency) {
+ this.citationKeyDependency.set(citationKeyDependency);
+ }
+
public String getRegularExpression() {
+ return regularExpression.getValue();
+ }
+
+ public StringProperty regularExpressionProperty() {
return regularExpression;
}
+ public void setRegularExpression(String regularExpression) {
+ this.regularExpression.set(regularExpression);
+ }
+
public boolean shouldAskAutoNamingPdfs() {
+ return askAutoNamingPdfs.getValue();
+ }
+
+ public BooleanProperty askAutoNamingPdfsProperty() {
return askAutoNamingPdfs;
}
- public AutoLinkPreferences withAskAutoNamingPdfs(boolean shouldAskAutoNamingPdfs) {
- this.askAutoNamingPdfs = shouldAskAutoNamingPdfs;
- return this;
+ public void setAskAutoNamingPdfs(boolean askAutoNamingPdfs) {
+ this.askAutoNamingPdfs.set(askAutoNamingPdfs);
}
public Character getKeywordDelimiter() {
+ return keywordDelimiter.getValue();
+ }
+
+ public SimpleObjectProperty keywordDelimiterProperty() {
return keywordDelimiter;
}
+
+ public void setKeywordDelimiter(Character keywordDelimiter) {
+ this.keywordDelimiter.set(keywordDelimiter);
+ }
}
diff --git a/src/main/java/org/jabref/logic/util/io/FileUtil.java b/src/main/java/org/jabref/logic/util/io/FileUtil.java
index 29279cc2731..369646b7f45 100644
--- a/src/main/java/org/jabref/logic/util/io/FileUtil.java
+++ b/src/main/java/org/jabref/logic/util/io/FileUtil.java
@@ -24,8 +24,10 @@
import org.jabref.logic.citationkeypattern.BracketedPattern;
import org.jabref.model.database.BibDatabase;
+import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.util.OptionalUtil;
+import org.jabref.preferences.PreferencesService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -374,4 +376,11 @@ public static boolean isBibFile(Path file) {
public static boolean isPDFFile(Path file) {
return getFileExtension(file).filter(type -> "pdf".equals(type)).isPresent();
}
+
+ /**
+ * @return Path of current panel database directory or the standard working directory in case the datbase was not saved yet
+ */
+ public static Path getInitialDirectory(BibDatabaseContext databaseContext, PreferencesService preferencesService) {
+ return databaseContext.getDatabasePath().map(Path::getParent).orElse(preferencesService.getWorkingDir());
+ }
}
diff --git a/src/main/java/org/jabref/migrations/PreferencesMigrations.java b/src/main/java/org/jabref/migrations/PreferencesMigrations.java
index 3d02cab3e72..6ff137d6d0f 100644
--- a/src/main/java/org/jabref/migrations/PreferencesMigrations.java
+++ b/src/main/java/org/jabref/migrations/PreferencesMigrations.java
@@ -170,7 +170,7 @@ private static void upgradeStoredBibEntryTypes(JabRefPreferences prefs, Preferen
// skip further processing as prefs already have been migrated
} else {
LOGGER.info("Migrating old custom entry types.");
- CustomEntryTypePreferenceMigration.upgradeStoredBibEntryTypes(prefs.getDefaultBibDatabaseMode());
+ CustomEntryTypePreferenceMigration.upgradeStoredBibEntryTypes(prefs.getGeneralPreferences().getDefaultBibDatabaseMode());
}
} catch (BackingStoreException ex) {
LOGGER.error("Migrating old custom entry types failed.", ex);
diff --git a/src/main/java/org/jabref/model/database/BibDatabase.java b/src/main/java/org/jabref/model/database/BibDatabase.java
index 4349e371db9..1fdb22ee890 100644
--- a/src/main/java/org/jabref/model/database/BibDatabase.java
+++ b/src/main/java/org/jabref/model/database/BibDatabase.java
@@ -59,8 +59,16 @@ public class BibDatabase {
// All file contents below the last entry in the file
private String epilog = "";
+
private String sharedDatabaseID;
+ private String newLineSeparator = System.lineSeparator();
+
+ public BibDatabase(List entries, String newLineSeparator) {
+ this(entries);
+ this.newLineSeparator = newLineSeparator;
+ }
+
public BibDatabase(List entries) {
this();
insertEntries(entries);
@@ -622,4 +630,20 @@ public long getNumberOfCitationKeyOccurrences(String key) {
public boolean isDuplicateCitationKeyExisting(String key) {
return getNumberOfCitationKeyOccurrences(key) > 1;
}
+
+ /**
+ * Set the newline separator.
+ * @param newLineSeparator
+ */
+ public void setNewLineSeparator(String newLineSeparator) {
+ this.newLineSeparator = newLineSeparator;
+ }
+
+ /**
+ * Returns the string used to indicate a linebreak
+ */
+ public String getNewLineSeparator() {
+ return newLineSeparator;
+ }
+
}
diff --git a/src/main/java/org/jabref/model/database/BibDatabaseContext.java b/src/main/java/org/jabref/model/database/BibDatabaseContext.java
index b0c2b96bce3..b632d02260c 100644
--- a/src/main/java/org/jabref/model/database/BibDatabaseContext.java
+++ b/src/main/java/org/jabref/model/database/BibDatabaseContext.java
@@ -144,14 +144,14 @@ public List getFileDirectories(FilePreferences preferences) {
preferences.getFileDirectory().ifPresent(fileDirs::add);
// 4. BIB file directory
- if (preferences.shouldStoreFilesRelativeToBib()) {
+ if (preferences.shouldStoreFilesRelativeToBibFile()) {
getDatabasePath().ifPresent(dbPath -> {
Path parentPath = dbPath.getParent();
if (parentPath == null) {
parentPath = Path.of(System.getProperty("user.dir"));
}
Objects.requireNonNull(parentPath, "BibTeX database parent path is null");
- fileDirs.add(0, parentPath);
+ fileDirs.add(parentPath);
});
}
@@ -213,6 +213,14 @@ public List getEntries() {
return database.getEntries();
}
+ /**
+ * check if the database has any empty entries
+ * @return true if the database has any empty entries; otherwise false
+ */
+ public boolean hasEmptyEntries() {
+ return this.getEntries().stream().anyMatch(entry->entry.getFields().isEmpty());
+ }
+
public static Path getFulltextIndexBasePath() {
return Path.of(AppDirsFactory.getInstance().getUserDataDir(SEARCH_INDEX_BASE_PATH, SearchFieldConstants.VERSION, "org.jabref"));
}
@@ -221,7 +229,7 @@ public Path getFulltextIndexPath() {
Path appData = getFulltextIndexBasePath();
if (getDatabasePath().isPresent()) {
- LOGGER.info("Index path for {} is {}", getDatabasePath().get(), appData.toString());
+ LOGGER.info("Index path for {} is {}", getDatabasePath().get(), appData);
return appData.resolve(String.valueOf(this.getDatabasePath().get().hashCode()));
}
diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java
index e0311d8b615..546afc1916c 100644
--- a/src/main/java/org/jabref/model/entry/BibEntry.java
+++ b/src/main/java/org/jabref/model/entry/BibEntry.java
@@ -12,7 +12,6 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
-import java.util.regex.Pattern;
import javafx.beans.Observable;
import javafx.beans.property.ObjectProperty;
@@ -57,7 +56,6 @@ public class BibEntry implements Cloneable {
public static final EntryType DEFAULT_TYPE = StandardEntryType.Misc;
private static final Logger LOGGER = LoggerFactory.getLogger(BibEntry.class);
- private static final Pattern REMOVE_TRAILING_WHITESPACE = Pattern.compile("\\s+$");
private final SharedBibEntryData sharedBibEntryData;
/**
@@ -397,7 +395,7 @@ public Optional setType(EntryType newType, EntriesEventSource event
}
/**
- * Returns an set containing the names of all fields that are set for this particular entry.
+ * Returns a set containing the names of all fields that are set for this particular entry.
*
* @return a set of existing field names
*/
@@ -699,8 +697,7 @@ public void setParsedSerialization(String parsedSerialization) {
}
public void setCommentsBeforeEntry(String parsedComments) {
- // delete trailing whitespaces (between entry and text)
- this.commentsBeforeEntry = REMOVE_TRAILING_WHITESPACE.matcher(parsedComments).replaceFirst("");
+ this.commentsBeforeEntry = parsedComments;
}
public boolean hasChanged() {
diff --git a/src/main/java/org/jabref/model/entry/BibtexString.java b/src/main/java/org/jabref/model/entry/BibtexString.java
index 84e1518cf3e..6b7bd2c289b 100644
--- a/src/main/java/org/jabref/model/entry/BibtexString.java
+++ b/src/main/java/org/jabref/model/entry/BibtexString.java
@@ -145,22 +145,14 @@ public boolean hasChanged() {
*/
public String getUserComments() {
if (parsedSerialization != null) {
-
try {
// get the text before the string
String prolog = parsedSerialization.substring(0, parsedSerialization.indexOf('@'));
-
- // delete trailing whitespaces (between string and text)
- prolog = prolog.replaceFirst("\\s+$", "");
- // if there is any non whitespace text, write it with proper line separation
- if (prolog.length() > 0) {
- return prolog;
- }
+ return prolog;
} catch (StringIndexOutOfBoundsException ignore) {
// if this occurs a broken parsed serialization has been set, so just do nothing
}
}
-
return "";
}
diff --git a/src/main/java/org/jabref/model/entry/identifier/ArXivIdentifier.java b/src/main/java/org/jabref/model/entry/identifier/ArXivIdentifier.java
index 87d3dcc333a..9b3949bd1e4 100644
--- a/src/main/java/org/jabref/model/entry/identifier/ArXivIdentifier.java
+++ b/src/main/java/org/jabref/model/entry/identifier/ArXivIdentifier.java
@@ -37,36 +37,34 @@ public class ArXivIdentifier implements Identifier {
public static Optional parse(String value) {
String identifier = value.replaceAll(" ", "");
- Pattern identifierPattern = Pattern.compile("(" + ARXIV_PREFIX + ")?\\s?:?\\s?(?\\d{4}.\\d{4,5})(v(?\\d+))?\\s?(\\[(?\\S+)\\])?");
+ Pattern identifierPattern = Pattern.compile("(" + ARXIV_PREFIX + ")?\\s?:?\\s?(?\\d{4}\\.\\d{4,5})(v(?\\d+))?\\s?(\\[(?\\S+)\\])?");
Matcher identifierMatcher = identifierPattern.matcher(identifier);
if (identifierMatcher.matches()) {
- String id = identifierMatcher.group("id");
- String classification = identifierMatcher.group("classification");
- if (classification == null) {
- classification = "";
- }
- String version = identifierMatcher.group("version");
- if (version == null) {
- version = "";
- }
- return Optional.of(new ArXivIdentifier(id, version, classification));
+ return getArXivIdentifier(identifierMatcher);
}
Pattern oldIdentifierPattern = Pattern.compile("(" + ARXIV_PREFIX + ")?\\s?:?\\s?(?(?[a-z\\-]+(\\.[A-Z]{2})?)/\\d{7})(v(?\\d+))?");
Matcher oldIdentifierMatcher = oldIdentifierPattern.matcher(identifier);
if (oldIdentifierMatcher.matches()) {
- String id = oldIdentifierMatcher.group("id");
- String classification = oldIdentifierMatcher.group("classification");
- String version = oldIdentifierMatcher.group("version");
- if (version == null) {
- version = "";
- }
- return Optional.of(new ArXivIdentifier(id, version, classification));
+ return getArXivIdentifier(oldIdentifierMatcher);
}
return Optional.empty();
}
+ private static Optional getArXivIdentifier(Matcher matcher) {
+ String id = matcher.group("id");
+ String classification = matcher.group("classification");
+ if (classification == null) {
+ classification = "";
+ }
+ String version = matcher.group("version");
+ if (version == null) {
+ version = "";
+ }
+ return Optional.of(new ArXivIdentifier(id, version, classification));
+ }
+
public Optional getClassification() {
if (classification.isEmpty()) {
return Optional.empty();
@@ -123,7 +121,7 @@ public String getNormalizedWithoutVersion() {
@Override
public Optional getExternalURI() {
try {
- return Optional.of(new URI("https://arxiv.org/abs/" + identifier));
+ return Optional.of(new URI("https://arxiv.org/abs/" + getNormalized()));
} catch (URISyntaxException e) {
return Optional.empty();
}
diff --git a/src/main/java/org/jabref/model/entry/identifier/DOI.java b/src/main/java/org/jabref/model/entry/identifier/DOI.java
index a9500406903..f8285d6dc93 100644
--- a/src/main/java/org/jabref/model/entry/identifier/DOI.java
+++ b/src/main/java/org/jabref/model/entry/identifier/DOI.java
@@ -20,11 +20,10 @@
public class DOI implements Identifier {
public static final URI AGENCY_RESOLVER = URI.create("https://doi.org/doiRA");
+ public static final URI RESOLVER = URI.create("https://doi.org/");
private static final Logger LOGGER = LoggerFactory.getLogger(DOI.class);
- private static final URI RESOLVER = URI.create("https://doi.org/");
-
// Regex
// (see http://www.doi.org/doi_handbook/2_Numbering.html)
private static final String DOI_EXP = ""
@@ -89,6 +88,14 @@ public class DOI implements Identifier {
private static final Pattern FIND_SHORT_DOI_SHORTCUT = Pattern.compile(IN_TEXT_SHORT_DOI_SHORTCUT, Pattern.CASE_INSENSITIVE); // eg doi.org/bfrhmx (no "10/")
private static final Pattern EXACT_SHORT_DOI_PATT = Pattern.compile(SHORT_DOI_EXP_PREFIX + SHORT_DOI_EXP, Pattern.CASE_INSENSITIVE);
private static final Pattern FIND_SHORT_DOI_PATT = Pattern.compile("(?:https?://[^\\s]+?)?" + FIND_SHORT_DOI_EXP, Pattern.CASE_INSENSITIVE);
+
+ // See https://www.baeldung.com/java-regex-s-splus for explanation of \\s+
+ // See https://stackoverflow.com/questions/3203190/regex-any-ascii-character for the regexp that includes ASCII characters only
+ // Another reference for regexp for ASCII characters: https://howtodoinjava.com/java/regex/java-clean-ascii-text-non-printable-chars/
+ private static final String CHARS_TO_REMOVE = "[\\s+" // remove white space characters, i.e, \t, \n, \x0B, \f, \r . + is a greedy quantifier
+ + "[^\\x00-\\x7F]" // strips off all non-ASCII characters
+ + "]";
+
// DOI
private final String doi;
// Short DOI
@@ -152,8 +159,9 @@ public DOI(String doi) {
*/
public static Optional parse(String doi) {
try {
- String cleanedDOI = doi.trim();
- cleanedDOI = doi.replaceAll(" ", "");
+ String cleanedDOI = doi;
+ cleanedDOI = cleanedDOI.replaceAll(CHARS_TO_REMOVE, "");
+
return Optional.of(new DOI(cleanedDOI));
} catch (IllegalArgumentException | NullPointerException e) {
return Optional.empty();
@@ -230,6 +238,8 @@ public boolean isShortDoi() {
*/
@Override
public Optional getExternalURI() {
+ // TODO: We need dependency injection here. It should never happen that this method is called.
+ // Always, the user preferences should be honored --> #getExternalURIWithCustomBase
return getExternalURIFromBase(RESOLVER);
}
diff --git a/src/main/java/org/jabref/model/entry/identifier/Eprint.java b/src/main/java/org/jabref/model/entry/identifier/Eprint.java
deleted file mode 100644
index fb9b27ab443..00000000000
--- a/src/main/java/org/jabref/model/entry/identifier/Eprint.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package org.jabref.model.entry.identifier;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.jabref.model.entry.field.Field;
-import org.jabref.model.entry.field.StandardField;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Class for working with Eprint identifiers
- *
- * See also https://arxiv.org/help/arxiv_identifier and https://arxiv.org/hypertex/bibstyles/
- */
-public class Eprint implements Identifier {
- public static final URI RESOLVER = URI.create("https://arxiv.org");
- private static final Logger LOGGER = LoggerFactory.getLogger(Eprint.class);
-
- // Regex
- // (see https://arxiv.org/help/arxiv_identifier)
- private static final String EPRINT_EXP = ""
- + "(?:arXiv:)?" // optional prefix
- + "(" // begin group \1
- + "\\d{4}" // YYMM
- + "\\." // divider
- + "\\d{4,5}" // number
- + "(v\\d+)?" // optional version
- + "|" // old id
- + ".+" // archive
- + "(\\.\\w{2})?" // optional subject class
- + "\\/" // divider
- + "\\d{7}" // number
- + ")"; // end group \1
- private static final String HTTP_EXP = "https?://[^\\s]+?" + EPRINT_EXP;
- // Pattern
- private static final Pattern EXACT_EPRINT_PATT = Pattern.compile("^(?:https?://[^\\s]+?)?" + EPRINT_EXP + "$", Pattern.CASE_INSENSITIVE);
-
- // DOI
- private final String eprint;
-
- /**
- * Creates a Eprint from various schemes including URL.
- *
- * @param eprint the Eprint identifier string
- * @throws NullPointerException if eprint is null
- * @throws IllegalArgumentException if eprint does not include a valid Eprint identifier
- */
- public Eprint(String eprint) {
- Objects.requireNonNull(eprint);
-
- // Remove whitespace
- String trimmedId = eprint.trim();
-
- // HTTP URL decoding
- if (eprint.matches(HTTP_EXP)) {
- try {
- // decodes path segment
- URI url = new URI(trimmedId);
- trimmedId = url.getScheme() + "://" + url.getHost() + url.getPath();
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException(eprint + " is not a valid HTTP Eprint identifier.");
- }
- }
-
- // Extract DOI
- Matcher matcher = EXACT_EPRINT_PATT.matcher(trimmedId);
- if (matcher.find()) {
- // match only group \1
- this.eprint = matcher.group(1);
- } else {
- throw new IllegalArgumentException(trimmedId + " is not a valid Eprint identifier.");
- }
- }
-
- /**
- * Creates an Optional<Eprint> from various schemes including URL.
- *
- * Useful for suppressing the IllegalArgumentException of the Constructor
- * and checking for Optional.isPresent() instead.
- *
- * @param eprint the Eprint string
- * @return an Optional containing the Eprint or an empty Optional
- */
- public static Optional build(String eprint) {
- try {
- return Optional.ofNullable(new Eprint(eprint));
- } catch (IllegalArgumentException | NullPointerException e) {
- return Optional.empty();
- }
- }
-
- /**
- * Return a URI presentation for the Eprint identifier
- *
- * @return an encoded URI representation of the Eprint identifier
- */
- @Override
- public Optional getExternalURI() {
- try {
- URI uri = new URI(RESOLVER.getScheme(), RESOLVER.getHost(), "/abs/" + eprint, null);
- return Optional.of(uri);
- } catch (URISyntaxException e) {
- // should never happen
- LOGGER.error(eprint + " could not be encoded as URI.", e);
- return Optional.empty();
- }
- }
-
- /**
- * Return an ASCII URL presentation for the Eprint identifier
- *
- * @return an encoded URL representation of the Eprint identifier
- */
- public String getURIAsASCIIString() {
- return getExternalURI().map(URI::toASCIIString).orElse("");
- }
-
- /**
- * Return the plain Eprint identifier
- *
- * @return the plain Eprint value.
- */
- public String getEprint() {
- return eprint;
- }
-
- @Override
- public Field getDefaultField() {
- return StandardField.EPRINT;
- }
-
- @Override
- public String getNormalized() {
- return eprint;
- }
-}
diff --git a/src/main/java/org/jabref/model/entry/identifier/IacrEprint.java b/src/main/java/org/jabref/model/entry/identifier/IacrEprint.java
new file mode 100644
index 00000000000..c385b9316ab
--- /dev/null
+++ b/src/main/java/org/jabref/model/entry/identifier/IacrEprint.java
@@ -0,0 +1,77 @@
+package org.jabref.model.entry.identifier;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.StandardField;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IacrEprint implements Identifier {
+ public static final URI RESOLVER = URI.create("https://ia.cr");
+ private static final Logger LOGGER = LoggerFactory.getLogger(IacrEprint.class);
+
+ private static final String IACR_EPRINT_EXP = "\\d{4}\\/\\d{3,5}";
+ private final String iacrEprint;
+
+ IacrEprint(String iacrEprint) {
+ Objects.requireNonNull(iacrEprint);
+
+ String trimmedId = iacrEprint.trim();
+
+ if (matchesExcepted(trimmedId)) {
+ Matcher matcher = Pattern.compile(IACR_EPRINT_EXP).matcher(trimmedId);
+ matcher.find();
+ this.iacrEprint = matcher.group(0);
+ } else {
+ throw new IllegalArgumentException(trimmedId + " is not a valid IacrEprint identifier.");
+ }
+ }
+
+ private static boolean matchesExcepted(String identifier) {
+ return identifier.matches(
+ "(https\\:\\/\\/)?(ia\\.cr\\/|eprint\\.iacr\\.org\\/)?" + IACR_EPRINT_EXP
+ );
+ }
+
+ public static Optional parse(String identifier) {
+ String trimmed = identifier.strip();
+ try {
+ return Optional.of(new IacrEprint(trimmed));
+ } catch (IllegalArgumentException illegalArgumentException) {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public String getNormalized() {
+ return iacrEprint;
+ }
+
+ @Override
+ public Field getDefaultField() {
+ return StandardField.EPRINT;
+ }
+
+ @Override
+ public Optional getExternalURI() {
+ try {
+ URI uri = new URI(RESOLVER.getScheme(), RESOLVER.getHost(), "/" + iacrEprint, null);
+ return Optional.of(uri);
+ } catch (URISyntaxException e) {
+ // should never happen
+ LOGGER.error(iacrEprint + " could not be encoded as URI.", e);
+ return Optional.empty();
+ }
+ }
+
+ public String getAsciiUrl() {
+ return getExternalURI().map(URI::toASCIIString).orElse("");
+ }
+}
diff --git a/src/main/java/org/jabref/model/metadata/SaveOrderConfig.java b/src/main/java/org/jabref/model/metadata/SaveOrderConfig.java
index 26720236083..9c79b0414aa 100644
--- a/src/main/java/org/jabref/model/metadata/SaveOrderConfig.java
+++ b/src/main/java/org/jabref/model/metadata/SaveOrderConfig.java
@@ -33,12 +33,12 @@ public String toString() {
return name;
}
- public static SaveOrderConfig.OrderType fromBooleans(boolean saveInSpecifiedOrder, boolean saveInTableOrder) {
- SaveOrderConfig.OrderType orderType = SaveOrderConfig.OrderType.ORIGINAL;
+ public static SaveOrderConfig.OrderType fromBooleans(boolean saveInSpecifiedOrder, boolean saveInOriginalOrder) {
+ SaveOrderConfig.OrderType orderType = SaveOrderConfig.OrderType.TABLE;
if (saveInSpecifiedOrder) {
orderType = SaveOrderConfig.OrderType.SPECIFIED;
- } else if (saveInTableOrder) {
- orderType = SaveOrderConfig.OrderType.TABLE;
+ } else if (saveInOriginalOrder) {
+ orderType = SaveOrderConfig.OrderType.ORIGINAL;
}
return orderType;
@@ -67,7 +67,7 @@ private SaveOrderConfig(List data) {
}
try {
- this.orderType = OrderType.valueOf(data.get(0));
+ this.orderType = OrderType.valueOf(data.get(0).toUpperCase());
} catch (IllegalArgumentException ex) {
if (data.size() > 1 && data.size() % 2 == 1) {
LOGGER.warn("Could not parse sort order: {} - trying to parse the sort criteria", data.get(0));
diff --git a/src/main/java/org/jabref/model/strings/StringUtil.java b/src/main/java/org/jabref/model/strings/StringUtil.java
index cb31437d442..ab4266733c7 100644
--- a/src/main/java/org/jabref/model/strings/StringUtil.java
+++ b/src/main/java/org/jabref/model/strings/StringUtil.java
@@ -394,6 +394,7 @@ private static String removeSingleBracesAroundCapitals(String s) {
/**
* Replaces all platform-dependent line breaks by OS.NEWLINE line breaks.
+ * AKA normalize newlines
*
* We do NOT use UNIX line breaks as the user explicitly configures its linebreaks and this method is used in bibtex field writing
*
diff --git a/src/main/java/org/jabref/preferences/FilePreferences.java b/src/main/java/org/jabref/preferences/FilePreferences.java
index 28ca322e925..e2a7e597eae 100644
--- a/src/main/java/org/jabref/preferences/FilePreferences.java
+++ b/src/main/java/org/jabref/preferences/FilePreferences.java
@@ -3,77 +3,103 @@
import java.nio.file.Path;
import java.util.Optional;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
import org.jabref.model.strings.StringUtil;
public class FilePreferences {
public static final String[] DEFAULT_FILENAME_PATTERNS = new String[] {"[bibtexkey]", "[bibtexkey] - [title]"};
- private final String user;
- private final String mainFileDirectory;
- private final boolean shouldStoreFilesRelativeToBibFile;
- private final String fileNamePattern;
- private final String fileDirPattern;
- private boolean shouldDownloadLinkedFiles;
- private final boolean shouldSearchFilesOnOpen;
- private final boolean shouldOpenBrowseOnCreate;
+ private final StringProperty user = new SimpleStringProperty();
+ private final SimpleStringProperty mainFileDirectory = new SimpleStringProperty();
+ private final BooleanProperty storeFilesRelativeToBibFile = new SimpleBooleanProperty();
+ private final StringProperty fileNamePattern = new SimpleStringProperty();
+ private final StringProperty fileDirectoryPattern = new SimpleStringProperty();
+ private final BooleanProperty downloadLinkedFiles = new SimpleBooleanProperty();
public FilePreferences(String user,
String mainFileDirectory,
- boolean shouldStoreFilesRelativeToBibFile,
+ boolean storeFilesRelativeToBibFile,
String fileNamePattern,
- String fileDirPattern,
- boolean shouldDownloadLinkedFiles,
- boolean shouldSearchFilesOnOpen,
- boolean shouldOpenBrowseOnCreate) {
- this.user = user;
- this.mainFileDirectory = mainFileDirectory;
- this.shouldStoreFilesRelativeToBibFile = shouldStoreFilesRelativeToBibFile;
- this.fileNamePattern = fileNamePattern;
- this.fileDirPattern = fileDirPattern;
- this.shouldDownloadLinkedFiles = shouldDownloadLinkedFiles;
- this.shouldSearchFilesOnOpen = shouldSearchFilesOnOpen;
- this.shouldOpenBrowseOnCreate = shouldOpenBrowseOnCreate;
+ String fileDirectoryPattern,
+ boolean downloadLinkedFiles) {
+ this.user.setValue(user);
+ this.mainFileDirectory.setValue(mainFileDirectory);
+ this.storeFilesRelativeToBibFile.setValue(storeFilesRelativeToBibFile);
+ this.fileNamePattern.setValue(fileNamePattern);
+ this.fileDirectoryPattern.setValue(fileDirectoryPattern);
+ this.downloadLinkedFiles.setValue(downloadLinkedFiles);
}
- public String getUser() {
- return user;
+ public String getUser() { // Read only
+ return user.getValue();
}
public Optional getFileDirectory() {
- if (StringUtil.isBlank(mainFileDirectory)) {
+ if (StringUtil.isBlank(mainFileDirectory.getValue())) {
return Optional.empty();
} else {
- return Optional.of(Path.of(mainFileDirectory));
+ return Optional.of(Path.of(mainFileDirectory.getValue()));
}
}
- public boolean shouldStoreFilesRelativeToBib() {
- return shouldStoreFilesRelativeToBibFile;
+ public StringProperty mainFileDirectoryProperty() {
+ return mainFileDirectory;
+ }
+
+ public void setMainFileDirectory(String mainFileDirectory) {
+ this.mainFileDirectory.set(mainFileDirectory);
+ }
+
+ public boolean shouldStoreFilesRelativeToBibFile() {
+ return storeFilesRelativeToBibFile.get();
+ }
+
+ public BooleanProperty storeFilesRelativeToBibFileProperty() {
+ return storeFilesRelativeToBibFile;
+ }
+
+ public void setStoreFilesRelativeToBibFile(boolean shouldStoreFilesRelativeToBibFile) {
+ this.storeFilesRelativeToBibFile.set(shouldStoreFilesRelativeToBibFile);
}
public String getFileNamePattern() {
+ return fileNamePattern.get();
+ }
+
+ public StringProperty fileNamePatternProperty() {
return fileNamePattern;
}
+ public void setFileNamePattern(String fileNamePattern) {
+ this.fileNamePattern.set(fileNamePattern);
+ }
+
public String getFileDirectoryPattern() {
- return fileDirPattern;
+ return fileDirectoryPattern.get();
}
- public boolean shouldDownloadLinkedFiles() {
- return shouldDownloadLinkedFiles;
+ public StringProperty fileDirectoryPatternProperty() {
+ return fileDirectoryPattern;
+ }
+
+ public void setFileDirectoryPattern(String fileDirectoryPattern) {
+ this.fileDirectoryPattern.set(fileDirectoryPattern);
}
- public FilePreferences withShouldDownloadLinkedFiles(boolean newShouldDownloadLinkedFiles) {
- this.shouldDownloadLinkedFiles = newShouldDownloadLinkedFiles;
- return this;
+ public boolean shouldDownloadLinkedFiles() {
+ return downloadLinkedFiles.get();
}
- public boolean shouldSearchFilesOnOpen() {
- return shouldSearchFilesOnOpen;
+ public BooleanProperty downloadLinkedFilesProperty() {
+ return downloadLinkedFiles;
}
- public boolean shouldOpenBrowseOnCreate() {
- return shouldOpenBrowseOnCreate;
+ public void setDownloadLinkedFiles(boolean shouldDownloadLinkedFiles) {
+ this.downloadLinkedFiles.set(shouldDownloadLinkedFiles);
}
}
diff --git a/src/main/java/org/jabref/preferences/GuiPreferences.java b/src/main/java/org/jabref/preferences/GuiPreferences.java
index 7aaeddd9417..3041231a2f3 100644
--- a/src/main/java/org/jabref/preferences/GuiPreferences.java
+++ b/src/main/java/org/jabref/preferences/GuiPreferences.java
@@ -3,18 +3,27 @@
import java.nio.file.Path;
import java.util.List;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
public class GuiPreferences {
- private final double positionX;
- private final double positionY;
- private final double sizeX;
- private final double sizeY;
+ private final DoubleProperty positionX;
+ private final DoubleProperty positionY;
+ private final DoubleProperty sizeX;
+ private final DoubleProperty sizeY;
- private final boolean windowMaximised;
+ private final BooleanProperty windowMaximised;
- private final boolean shouldOpenLastEdited;
- private List lastFilesOpened;
- private Path lastFocusedFile;
- private double sidePaneWidth;
+ private final BooleanProperty shouldOpenLastEdited;
+ private final ObservableList lastFilesOpened;
+ private final ObjectProperty lastFocusedFile;
+ private final DoubleProperty sidePaneWidth;
public GuiPreferences(double positionX,
double positionY,
@@ -24,65 +33,118 @@ public GuiPreferences(double positionX,
boolean shouldOpenLastEdited,
List lastFilesOpened,
Path lastFocusedFile, double sidePaneWidth) {
- this.positionX = positionX;
- this.positionY = positionY;
- this.sizeX = sizeX;
- this.sizeY = sizeY;
- this.windowMaximised = windowMaximised;
- this.shouldOpenLastEdited = shouldOpenLastEdited;
- this.lastFilesOpened = lastFilesOpened;
- this.lastFocusedFile = lastFocusedFile;
- this.sidePaneWidth = sidePaneWidth;
+ this.positionX = new SimpleDoubleProperty(positionX);
+ this.positionY = new SimpleDoubleProperty(positionY);
+ this.sizeX = new SimpleDoubleProperty(sizeX);
+ this.sizeY = new SimpleDoubleProperty(sizeY);
+ this.windowMaximised = new SimpleBooleanProperty(windowMaximised);
+ this.shouldOpenLastEdited = new SimpleBooleanProperty(shouldOpenLastEdited);
+ this.lastFilesOpened = FXCollections.observableArrayList(lastFilesOpened);
+ this.lastFocusedFile = new SimpleObjectProperty<>(lastFocusedFile);
+ this.sidePaneWidth = new SimpleDoubleProperty(sidePaneWidth);
}
public double getPositionX() {
+ return positionX.get();
+ }
+
+ public DoubleProperty positionXProperty() {
return positionX;
}
+ public void setPositionX(double positionX) {
+ this.positionX.set(positionX);
+ }
+
public double getPositionY() {
+ return positionY.get();
+ }
+
+ public DoubleProperty positionYProperty() {
return positionY;
}
+ public void setPositionY(double positionY) {
+ this.positionY.set(positionY);
+ }
+
public double getSizeX() {
+ return sizeX.get();
+ }
+
+ public DoubleProperty sizeXProperty() {
return sizeX;
}
+ public void setSizeX(double sizeX) {
+ this.sizeX.set(sizeX);
+ }
+
public double getSizeY() {
+ return sizeY.get();
+ }
+
+ public DoubleProperty sizeYProperty() {
return sizeY;
}
+ public void setSizeY(double sizeY) {
+ this.sizeY.set(sizeY);
+ }
+
public boolean isWindowMaximised() {
+ return windowMaximised.get();
+ }
+
+ public BooleanProperty windowMaximisedProperty() {
return windowMaximised;
}
+ public void setWindowMaximised(boolean windowMaximised) {
+ this.windowMaximised.set(windowMaximised);
+ }
+
public boolean shouldOpenLastEdited() {
+ return shouldOpenLastEdited.get();
+ }
+
+ public BooleanProperty openLastEditedProperty() {
return shouldOpenLastEdited;
}
- public List getLastFilesOpened() {
+ public void setOpenLastEdited(boolean shouldOpenLastEdited) {
+ this.shouldOpenLastEdited.set(shouldOpenLastEdited);
+ }
+
+ public ObservableList getLastFilesOpened() {
return lastFilesOpened;
}
- public GuiPreferences withLastFilesOpened(List lastFilesOpened) {
- this.lastFilesOpened = lastFilesOpened;
- return this;
+ public void setLastFilesOpened(List files) {
+ lastFilesOpened.setAll(files);
}
public Path getLastFocusedFile() {
+ return lastFocusedFile.get();
+ }
+
+ public ObjectProperty lastFocusedFileProperty() {
return lastFocusedFile;
}
- public GuiPreferences withLastFocusedFile(Path lastFocusedFile) {
- this.lastFocusedFile = lastFocusedFile;
- return this;
+ public void setLastFocusedFile(Path lastFocusedFile) {
+ this.lastFocusedFile.set(lastFocusedFile);
}
public double getSidePaneWidth() {
+ return sidePaneWidth.get();
+ }
+
+ public DoubleProperty sidePaneWidthProperty() {
return sidePaneWidth;
}
- public GuiPreferences withSidePaneWidth(double sidePaneWidth) {
- this.sidePaneWidth = sidePaneWidth;
- return this;
+ public void setSidePaneWidth(double sidePaneWidth) {
+ this.sidePaneWidth.set(sidePaneWidth);
}
}
diff --git a/src/main/java/org/jabref/preferences/ImportExportPreferences.java b/src/main/java/org/jabref/preferences/ImportExportPreferences.java
index cbc808fd3cd..a8a8d8a4389 100644
--- a/src/main/java/org/jabref/preferences/ImportExportPreferences.java
+++ b/src/main/java/org/jabref/preferences/ImportExportPreferences.java
@@ -2,85 +2,134 @@
import java.nio.file.Path;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
public class ImportExportPreferences {
- private final String nonWrappableFields;
- private final boolean resolveStringsForStandardBibtexFields;
- private final boolean resolveStringsForAllStrings;
- private final String nonResolvableFields;
- private final NewLineSeparator newLineSeparator;
- private final boolean alwaysReformatOnSave;
- private Path importWorkingDirectory;
- private String lastExportExtension;
- private Path exportWorkingDirectory;
+ private final StringProperty nonWrappableFields;
+ private final BooleanProperty resolveStringsForStandardBibtexFields;
+ private final BooleanProperty resolveStringsForAllStrings;
+ private final StringProperty nonResolvableFields;
+ private final BooleanProperty alwaysReformatOnSave;
+ private final ObjectProperty importWorkingDirectory;
+ private final StringProperty lastExportExtension;
+ private final ObjectProperty exportWorkingDirectory;
public ImportExportPreferences(String nonWrappableFields,
boolean resolveStringsForStandardBibtexFields,
boolean resolveStringsForAllStrings,
String nonResolvableFields,
- NewLineSeparator newLineSeparator,
boolean alwaysReformatOnSave,
Path importWorkingDirectory,
String lastExportExtension,
Path exportWorkingDirectory) {
- this.nonWrappableFields = nonWrappableFields;
- this.resolveStringsForStandardBibtexFields = resolveStringsForStandardBibtexFields;
- this.resolveStringsForAllStrings = resolveStringsForAllStrings;
- this.nonResolvableFields = nonResolvableFields;
- this.newLineSeparator = newLineSeparator;
- this.alwaysReformatOnSave = alwaysReformatOnSave;
- this.importWorkingDirectory = importWorkingDirectory;
- this.lastExportExtension = lastExportExtension;
- this.exportWorkingDirectory = exportWorkingDirectory;
+ this.nonWrappableFields = new SimpleStringProperty(nonWrappableFields);
+ this.resolveStringsForStandardBibtexFields = new SimpleBooleanProperty(resolveStringsForStandardBibtexFields);
+ this.resolveStringsForAllStrings = new SimpleBooleanProperty(resolveStringsForAllStrings);
+ this.nonResolvableFields = new SimpleStringProperty(nonResolvableFields);
+ this.alwaysReformatOnSave = new SimpleBooleanProperty(alwaysReformatOnSave);
+ this.importWorkingDirectory = new SimpleObjectProperty<>(importWorkingDirectory);
+ this.lastExportExtension = new SimpleStringProperty(lastExportExtension);
+ this.exportWorkingDirectory = new SimpleObjectProperty<>(exportWorkingDirectory);
}
public String getNonWrappableFields() {
+ return nonWrappableFields.get();
+ }
+
+ public StringProperty nonWrappableFieldsProperty() {
return nonWrappableFields;
}
+ public void setNonWrappableFields(String nonWrappableFields) {
+ this.nonWrappableFields.set(nonWrappableFields);
+ }
+
public boolean shouldResolveStringsForStandardBibtexFields() {
+ return resolveStringsForStandardBibtexFields.get();
+ }
+
+ public BooleanProperty resolveStringsForStandardBibtexFieldsProperty() {
return resolveStringsForStandardBibtexFields;
}
+ public void setResolveStringsForStandardBibtexFields(boolean resolveStringsForStandardBibtexFields) {
+ this.resolveStringsForStandardBibtexFields.set(resolveStringsForStandardBibtexFields);
+ }
+
public boolean shouldResolveStringsForAllStrings() {
+ return resolveStringsForAllStrings.get();
+ }
+
+ public BooleanProperty resolveStringsForAllStringsProperty() {
return resolveStringsForAllStrings;
}
+ public void setResolveStringsForAllStrings(boolean resolveStringsForAllStrings) {
+ this.resolveStringsForAllStrings.set(resolveStringsForAllStrings);
+ }
+
public String getNonResolvableFields() {
+ return nonResolvableFields.get();
+ }
+
+ public StringProperty nonResolvableFieldsProperty() {
return nonResolvableFields;
}
- public NewLineSeparator getNewLineSeparator() {
- return newLineSeparator;
+ public void setNonResolvableFields(String nonResolvableFields) {
+ this.nonResolvableFields.set(nonResolvableFields);
}
public boolean shouldAlwaysReformatOnSave() {
+ return alwaysReformatOnSave.get();
+ }
+
+ public BooleanProperty alwaysReformatOnSaveProperty() {
return alwaysReformatOnSave;
}
+ public void setAlwaysReformatOnSave(boolean alwaysReformatOnSave) {
+ this.alwaysReformatOnSave.set(alwaysReformatOnSave);
+ }
+
public Path getImportWorkingDirectory() {
+ return importWorkingDirectory.get();
+ }
+
+ public ObjectProperty importWorkingDirectoryProperty() {
return importWorkingDirectory;
}
- public ImportExportPreferences withImportWorkingDirectory(Path importWorkingDirectory) {
- this.importWorkingDirectory = importWorkingDirectory;
- return this;
+ public void setImportWorkingDirectory(Path importWorkingDirectory) {
+ this.importWorkingDirectory.set(importWorkingDirectory);
}
public String getLastExportExtension() {
+ return lastExportExtension.get();
+ }
+
+ public StringProperty lastExportExtensionProperty() {
return lastExportExtension;
}
- public ImportExportPreferences withLastExportExtension(String lastExportExtension) {
- this.lastExportExtension = lastExportExtension;
- return this;
+ public void setLastExportExtension(String lastExportExtension) {
+ this.lastExportExtension.set(lastExportExtension);
}
public Path getExportWorkingDirectory() {
+ return exportWorkingDirectory.get();
+ }
+
+ public ObjectProperty exportWorkingDirectoryProperty() {
return exportWorkingDirectory;
}
- public ImportExportPreferences withExportWorkingDirectory(Path exportWorkingDirectory) {
- this.exportWorkingDirectory = exportWorkingDirectory;
- return this;
+ public void setExportWorkingDirectory(Path exportWorkingDirectory) {
+ this.exportWorkingDirectory.set(exportWorkingDirectory);
}
}
diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java
index 325898a86c8..422f39d833a 100644
--- a/src/main/java/org/jabref/preferences/JabRefPreferences.java
+++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java
@@ -18,6 +18,7 @@
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
@@ -34,10 +35,10 @@
import java.util.stream.Stream;
import javafx.beans.InvalidationListener;
+import javafx.collections.SetChangeListener;
import javafx.scene.control.TableColumn.SortType;
import org.jabref.gui.Globals;
-import org.jabref.gui.SidePaneType;
import org.jabref.gui.autocompleter.AutoCompleteFirstNameMode;
import org.jabref.gui.autocompleter.AutoCompletePreferences;
import org.jabref.gui.desktop.JabRefDesktop;
@@ -53,6 +54,7 @@
import org.jabref.gui.maintable.MainTablePreferences;
import org.jabref.gui.mergeentries.MergeEntries;
import org.jabref.gui.search.SearchDisplayMode;
+import org.jabref.gui.sidepane.SidePaneType;
import org.jabref.gui.specialfields.SpecialFieldsPreferences;
import org.jabref.gui.util.Theme;
import org.jabref.logic.JabRefException;
@@ -107,6 +109,7 @@
import org.jabref.model.entry.types.EntryTypeFactory;
import org.jabref.model.metadata.SaveOrderConfig;
import org.jabref.model.push.PushToApplicationConstants;
+import org.jabref.model.search.rules.SearchRules;
import org.jabref.model.strings.StringUtil;
import com.tobiasdiez.easybind.EasyBind;
@@ -154,6 +157,7 @@ public class JabRefPreferences implements PreferencesService {
public static final String REFORMAT_FILE_ON_SAVE_AND_EXPORT = "reformatFileOnSaveAndExport";
public static final String EXPORT_IN_ORIGINAL_ORDER = "exportInOriginalOrder";
public static final String EXPORT_IN_SPECIFIED_ORDER = "exportInSpecifiedOrder";
+
public static final String EXPORT_PRIMARY_SORT_FIELD = "exportPriSort";
public static final String EXPORT_PRIMARY_SORT_DESCENDING = "exportPriDescending";
public static final String EXPORT_SECONDARY_SORT_FIELD = "exportSecSort";
@@ -259,7 +263,6 @@ public class JabRefPreferences implements PreferencesService {
public static final String KEY_GEN_FIRST_LETTER_A = "keyGenFirstLetterA";
public static final String ALLOW_INTEGER_EDITION_BIBTEX = "allowIntegerEditionBibtex";
public static final String LOCAL_AUTO_SAVE = "localAutoSave";
- public static final String RUN_AUTOMATIC_FILE_SEARCH = "runAutomaticFileSearch";
public static final String AUTOLINK_REG_EXP_SEARCH_EXPRESSION_KEY = "regExpSearchExpression";
public static final String AUTOLINK_USE_REG_EXP_SEARCH_KEY = "useRegExpSearch";
// bibLocAsPrimaryDir is a misleading antique variable name, we keep it for reason of compatibility
@@ -267,7 +270,6 @@ public class JabRefPreferences implements PreferencesService {
public static final String SELECTED_FETCHER_INDEX = "selectedFetcherIndex";
public static final String WEB_SEARCH_VISIBLE = "webSearchVisible";
public static final String GROUP_SIDEPANE_VISIBLE = "groupSidepaneVisible";
- public static final String ALLOW_FILE_AUTO_OPEN_BROWSE = "allowFileAutoOpenBrowse";
public static final String CUSTOM_TAB_NAME = "customTabName_";
public static final String CUSTOM_TAB_FIELDS = "customTabFields_";
public static final String ASK_AUTO_NAMING_PDFS_AGAIN = "AskAutoNamingPDFsAgain";
@@ -288,7 +290,7 @@ public class JabRefPreferences implements PreferencesService {
/**
* The OpenOffice/LibreOffice connection preferences are:
- * OO_PATH main directory for OO/LO installation, used to detect location on Win/OS X when using manual connect
+ * OO_PATH main directory for OO/LO installation, used to detect location on Win/macOS when using manual connect
* OO_EXECUTABLE_PATH path to soffice-file
* OO_JARS_PATH directory that contains juh.jar, jurt.jar, ridl.jar, unoil.jar
* OO_SYNC_WHEN_CITING true if the reference list is updated when adding a new citation
@@ -430,6 +432,13 @@ public class JabRefPreferences implements PreferencesService {
private ProtectedTermsPreferences protectedTermsPreferences;
private MrDlibPreferences mrDlibPreferences;
private EntryEditorPreferences entryEditorPreferences;
+ private FilePreferences filePreferences;
+ private GuiPreferences guiPreferences;
+ private RemotePreferences remotePreferences;
+ private ProxyPreferences proxyPreferences;
+ private SearchPreferences searchPreferences;
+ private AutoLinkPreferences autoLinkPreferences;
+ private ImportExportPreferences importExportPreferences;
// The constructor is made private to enforce this as a singleton class:
private JabRefPreferences() {
@@ -509,7 +518,7 @@ private JabRefPreferences() {
defaults.put(SIZE_X, 1024);
defaults.put(SIZE_Y, 768);
defaults.put(WINDOW_MAXIMISED, Boolean.TRUE);
- defaults.put(AUTO_RESIZE_MODE, Boolean.TRUE);
+ defaults.put(AUTO_RESIZE_MODE, Boolean.FALSE); // By default disable "Fit table horizontally on the screen"
defaults.put(ENTRY_EDITOR_HEIGHT, 0.65);
defaults.put(NAMES_AS_IS, Boolean.FALSE); // "Show names unchanged"
defaults.put(NAMES_FIRST_LAST, Boolean.FALSE); // "Show 'Firstname Lastname'"
@@ -522,7 +531,7 @@ private JabRefPreferences() {
defaults.put(REFORMAT_FILE_ON_SAVE_AND_EXPORT, Boolean.FALSE);
// export order
- defaults.put(EXPORT_IN_ORIGINAL_ORDER, Boolean.FALSE);
+ defaults.put(EXPORT_IN_ORIGINAL_ORDER, Boolean.TRUE);
defaults.put(EXPORT_IN_SPECIFIED_ORDER, Boolean.FALSE);
// export order: if EXPORT_IN_SPECIFIED_ORDER, then use following criteria
@@ -640,7 +649,6 @@ private JabRefPreferences() {
defaults.put(OVERRIDE_DEFAULT_FONT_SIZE, false);
defaults.put(AUTOLINK_EXACT_KEY_ONLY, Boolean.FALSE);
- defaults.put(RUN_AUTOMATIC_FILE_SEARCH, Boolean.FALSE);
defaults.put(LOCAL_AUTO_SAVE, Boolean.FALSE);
defaults.put(ALLOW_INTEGER_EDITION_BIBTEX, Boolean.FALSE);
// Curly brackets ({}) are the default delimiters, not quotes (") as these cause trouble when they appear within the field value:
@@ -649,7 +657,6 @@ private JabRefPreferences() {
defaults.put(KEY_GEN_ALWAYS_ADD_LETTER, Boolean.FALSE);
defaults.put(EMAIL_SUBJECT, Localization.lang("References"));
defaults.put(OPEN_FOLDERS_OF_ATTACHED_FILES, Boolean.FALSE);
- defaults.put(ALLOW_FILE_AUTO_OPEN_BROWSE, Boolean.TRUE);
defaults.put(WEB_SEARCH_VISIBLE, Boolean.TRUE);
defaults.put(GROUP_SIDEPANE_VISIBLE, Boolean.TRUE);
defaults.put(SELECTED_FETCHER_INDEX, 0);
@@ -1104,7 +1111,6 @@ public OpenOfficePreferences getOpenOfficePreferences() {
get(OO_PATH),
getBoolean(OO_USE_ALL_OPEN_BASES),
getBoolean(OO_SYNC_WHEN_CITING),
- getBoolean(OO_SHOW_PANEL),
getStringList(OO_EXTERNAL_STYLE_FILES),
get(OO_BIBLIOGRAPHY_STYLE_FILE));
}
@@ -1115,7 +1121,6 @@ public void setOpenOfficePreferences(OpenOfficePreferences openOfficePreferences
put(OO_PATH, openOfficePreferences.getInstallationPath());
putBoolean(OO_USE_ALL_OPEN_BASES, openOfficePreferences.getUseAllDatabases());
putBoolean(OO_SYNC_WHEN_CITING, openOfficePreferences.getSyncWhenCiting());
- putBoolean(OO_SHOW_PANEL, openOfficePreferences.getShowPanel());
putStringList(OO_EXTERNAL_STYLE_FILES, openOfficePreferences.getExternalStyles());
put(OO_BIBLIOGRAPHY_STYLE_FILE, openOfficePreferences.getCurrentStyle());
}
@@ -1133,7 +1138,7 @@ public void storeVersionPreferences(VersionPreferences versionPreferences) {
@Override
public JournalAbbreviationPreferences getJournalAbbreviationPreferences() {
- return new JournalAbbreviationPreferences(getStringList(EXTERNAL_JOURNAL_LISTS), getDefaultEncoding());
+ return new JournalAbbreviationPreferences(getStringList(EXTERNAL_JOURNAL_LISTS), getGeneralPreferences().getDefaultEncoding());
}
@Override
@@ -1317,43 +1322,27 @@ public void setLanguage(Language language) {
updateLanguage();
}
- @Override
- public Charset getDefaultEncoding() {
- return Charset.forName(get(DEFAULT_ENCODING));
- }
-
- /**
- * Returns the default BibDatabase mode, which can be either BIBTEX or BIBLATEX.
- *
- * @return the default BibDatabaseMode
- */
- @Override
- public BibDatabaseMode getDefaultBibDatabaseMode() {
- if (getBoolean(BIBLATEX_DEFAULT_MODE)) {
- return BibDatabaseMode.BIBLATEX;
- } else {
- return BibDatabaseMode.BIBTEX;
- }
- }
-
@Override
public GeneralPreferences getGeneralPreferences() {
if (Objects.nonNull(generalPreferences)) {
return generalPreferences;
}
+
generalPreferences = new GeneralPreferences(
- getDefaultEncoding(),
+ Charset.forName(get(DEFAULT_ENCODING)),
getBoolean(BIBLATEX_DEFAULT_MODE) ? BibDatabaseMode.BIBLATEX : BibDatabaseMode.BIBTEX,
getBoolean(WARN_ABOUT_DUPLICATES_IN_INSPECTION),
getBoolean(CONFIRM_DELETE),
getBoolean(MEMORY_STICK_MODE),
getBoolean(SHOW_ADVANCED_HINTS));
- EasyBind.subscribe(generalPreferences.defaultEncodingProperty(), newValue -> put(DEFAULT_ENCODING, newValue.name()));
- EasyBind.subscribe(generalPreferences.defaultBibDatabaseModeProperty(), newValue -> putBoolean(BIBLATEX_DEFAULT_MODE, (newValue == BibDatabaseMode.BIBLATEX)));
- EasyBind.subscribe(generalPreferences.isWarnAboutDuplicatesInInspectionProperty(), newValue -> putBoolean(WARN_ABOUT_DUPLICATES_IN_INSPECTION, newValue));
- EasyBind.subscribe(generalPreferences.confirmDeleteProperty(), newValue -> putBoolean(CONFIRM_DELETE, newValue));
- EasyBind.subscribe(generalPreferences.memoryStickModeProperty(), newValue -> putBoolean(MEMORY_STICK_MODE, newValue));
- EasyBind.subscribe(generalPreferences.showAdvancedHintsProperty(), newValue -> putBoolean(SHOW_ADVANCED_HINTS, newValue));
+
+ EasyBind.listen(generalPreferences.defaultEncodingProperty(), (obs, oldValue, newValue) -> put(DEFAULT_ENCODING, newValue.name()));
+ EasyBind.listen(generalPreferences.defaultBibDatabaseModeProperty(), (obs, oldValue, newValue) -> putBoolean(BIBLATEX_DEFAULT_MODE, (newValue == BibDatabaseMode.BIBLATEX)));
+ EasyBind.listen(generalPreferences.isWarnAboutDuplicatesInInspectionProperty(), (obs, oldValue, newValue) -> putBoolean(WARN_ABOUT_DUPLICATES_IN_INSPECTION, newValue));
+ EasyBind.listen(generalPreferences.confirmDeleteProperty(), (obs, oldValue, newValue) -> putBoolean(CONFIRM_DELETE, newValue));
+ EasyBind.listen(generalPreferences.memoryStickModeProperty(), (obs, oldValue, newValue) -> putBoolean(MEMORY_STICK_MODE, newValue));
+ EasyBind.listen(generalPreferences.showAdvancedHintsProperty(), (obs, oldValue, newValue) -> putBoolean(SHOW_ADVANCED_HINTS, newValue));
+
return generalPreferences;
}
@@ -1366,8 +1355,8 @@ public TelemetryPreferences getTelemetryPreferences() {
getBoolean(COLLECT_TELEMETRY),
!getBoolean(ALREADY_ASKED_TO_COLLECT_TELEMETRY) // mind the !
);
- EasyBind.subscribe(telemetryPreferences.collectTelemetryProperty(), newValue -> putBoolean(COLLECT_TELEMETRY, newValue));
- EasyBind.subscribe(telemetryPreferences.askToCollectTelemetryProperty(), newValue -> putBoolean(ALREADY_ASKED_TO_COLLECT_TELEMETRY, !newValue));
+ EasyBind.listen(telemetryPreferences.collectTelemetryProperty(), (obs, oldValue, newValue) -> putBoolean(COLLECT_TELEMETRY, newValue));
+ EasyBind.listen(telemetryPreferences.askToCollectTelemetryProperty(), (obs, oldValue, newValue) -> putBoolean(ALREADY_ASKED_TO_COLLECT_TELEMETRY, !newValue));
return telemetryPreferences;
}
@@ -1379,8 +1368,8 @@ public DOIPreferences getDOIPreferences() {
doiPreferences = new DOIPreferences(
getBoolean(USE_CUSTOM_DOI_URI),
get(BASE_DOI_URI));
- EasyBind.subscribe(doiPreferences.useCustomProperty(), newValue -> putBoolean(USE_CUSTOM_DOI_URI, newValue));
- EasyBind.subscribe(doiPreferences.defaultBaseURIProperty(), newValue -> put(BASE_DOI_URI, newValue));
+ EasyBind.listen(doiPreferences.useCustomProperty(), (obs, oldValue, newValue) -> putBoolean(USE_CUSTOM_DOI_URI, newValue));
+ EasyBind.listen(doiPreferences.defaultBaseURIProperty(), (obs, oldValue, newValue) -> put(BASE_DOI_URI, newValue));
return doiPreferences;
}
@@ -1393,9 +1382,9 @@ public OwnerPreferences getOwnerPreferences() {
getBoolean(USE_OWNER),
get(DEFAULT_OWNER),
getBoolean(OVERWRITE_OWNER));
- EasyBind.subscribe(ownerPreferences.useOwnerProperty(), newValue -> putBoolean(USE_OWNER, newValue));
- EasyBind.subscribe(ownerPreferences.defaultOwnerProperty(), newValue -> put(DEFAULT_OWNER, newValue));
- EasyBind.subscribe(ownerPreferences.overwriteOwnerProperty(), newValue -> putBoolean(OVERWRITE_OWNER, newValue));
+ EasyBind.listen(ownerPreferences.useOwnerProperty(), (obs, oldValue, newValue) -> putBoolean(USE_OWNER, newValue));
+ EasyBind.listen(ownerPreferences.defaultOwnerProperty(), (obs, oldValue, newValue) -> put(DEFAULT_OWNER, newValue));
+ EasyBind.listen(ownerPreferences.overwriteOwnerProperty(), (obs, oldValue, newValue) -> putBoolean(OVERWRITE_OWNER, newValue));
return ownerPreferences;
}
@@ -1411,8 +1400,8 @@ public TimestampPreferences getTimestampPreferences() {
FieldFactory.parseField(get(TIME_STAMP_FIELD)),
get(TIME_STAMP_FORMAT));
- EasyBind.subscribe(timestampPreferences.addCreationDateProperty(), newValue -> putBoolean(ADD_CREATION_DATE, newValue));
- EasyBind.subscribe(timestampPreferences.addModificationDateProperty(), newValue -> putBoolean(ADD_MODIFICATION_DATE, newValue));
+ EasyBind.listen(timestampPreferences.addCreationDateProperty(), (obs, oldValue, newValue) -> putBoolean(ADD_CREATION_DATE, newValue));
+ EasyBind.listen(timestampPreferences.addModificationDateProperty(), (obs, oldValue, newValue) -> putBoolean(ADD_MODIFICATION_DATE, newValue));
return timestampPreferences;
}
@@ -1589,15 +1578,15 @@ public EntryEditorPreferences getEntryEditorPreferences() {
getBoolean(ALLOW_INTEGER_EDITION_BIBTEX),
getDouble(ENTRY_EDITOR_HEIGHT));
- EasyBind.subscribe(entryEditorPreferences.entryEditorTabListProperty(), this::storeEntryEditorTabList);
- EasyBind.subscribe(entryEditorPreferences.shouldOpenOnNewEntryProperty(), newValue -> putBoolean(AUTO_OPEN_FORM, newValue));
- EasyBind.subscribe(entryEditorPreferences.shouldShowRecommendationsTabProperty(), newValue -> putBoolean(SHOW_RECOMMENDATIONS, newValue));
- EasyBind.subscribe(entryEditorPreferences.isMrdlibAcceptedProperty(), newValue -> putBoolean(ACCEPT_RECOMMENDATIONS, newValue));
- EasyBind.subscribe(entryEditorPreferences.shouldShowLatexCitationsTabProperty(), newValue -> putBoolean(SHOW_LATEX_CITATIONS, newValue));
- EasyBind.subscribe(entryEditorPreferences.showSourceTabByDefaultProperty(), newValue -> putBoolean(DEFAULT_SHOW_SOURCE, newValue));
- EasyBind.subscribe(entryEditorPreferences.enableValidationProperty(), newValue -> putBoolean(VALIDATE_IN_ENTRY_EDITOR, newValue));
- EasyBind.subscribe(entryEditorPreferences.allowIntegerEditionBibtexProperty(), newValue -> putBoolean(ALLOW_INTEGER_EDITION_BIBTEX, newValue));
- EasyBind.subscribe(entryEditorPreferences.dividerPositionProperty(), newValue -> putDouble(ENTRY_EDITOR_HEIGHT, newValue.doubleValue()));
+ EasyBind.listen(entryEditorPreferences.entryEditorTabListProperty(), (obs, oldValue, newValue) -> storeEntryEditorTabList(newValue));
+ EasyBind.listen(entryEditorPreferences.shouldOpenOnNewEntryProperty(), (obs, oldValue, newValue) -> putBoolean(AUTO_OPEN_FORM, newValue));
+ EasyBind.listen(entryEditorPreferences.shouldShowRecommendationsTabProperty(), (obs, oldValue, newValue) -> putBoolean(SHOW_RECOMMENDATIONS, newValue));
+ EasyBind.listen(entryEditorPreferences.isMrdlibAcceptedProperty(), (obs, oldValue, newValue) -> putBoolean(ACCEPT_RECOMMENDATIONS, newValue));
+ EasyBind.listen(entryEditorPreferences.shouldShowLatexCitationsTabProperty(), (obs, oldValue, newValue) -> putBoolean(SHOW_LATEX_CITATIONS, newValue));
+ EasyBind.listen(entryEditorPreferences.showSourceTabByDefaultProperty(), (obs, oldValue, newValue) -> putBoolean(DEFAULT_SHOW_SOURCE, newValue));
+ EasyBind.listen(entryEditorPreferences.enableValidationProperty(), (obs, oldValue, newValue) -> putBoolean(VALIDATE_IN_ENTRY_EDITOR, newValue));
+ EasyBind.listen(entryEditorPreferences.allowIntegerEditionBibtexProperty(), (obs, oldValue, newValue) -> putBoolean(ALLOW_INTEGER_EDITION_BIBTEX, newValue));
+ EasyBind.listen(entryEditorPreferences.dividerPositionProperty(), (obs, oldValue, newValue) -> putDouble(ENTRY_EDITOR_HEIGHT, newValue.doubleValue()));
return entryEditorPreferences;
}
@@ -1608,34 +1597,42 @@ public EntryEditorPreferences getEntryEditorPreferences() {
@Override
public RemotePreferences getRemotePreferences() {
- return new RemotePreferences(getInt(REMOTE_SERVER_PORT), getBoolean(USE_REMOTE_SERVER));
- }
+ if (Objects.nonNull(remotePreferences)) {
+ return remotePreferences;
+ }
- @Override
- public void storeRemotePreferences(RemotePreferences preferences) {
- putInt(REMOTE_SERVER_PORT, preferences.getPort());
- putBoolean(USE_REMOTE_SERVER, preferences.useRemoteServer());
+ remotePreferences = new RemotePreferences(
+ getInt(REMOTE_SERVER_PORT),
+ getBoolean(USE_REMOTE_SERVER));
+
+ EasyBind.listen(remotePreferences.portProperty(), (obs, oldValue, newValue) -> putInt(REMOTE_SERVER_PORT, newValue));
+ EasyBind.listen(remotePreferences.useRemoteServerProperty(), (obs, oldValue, newValue) -> putBoolean(USE_REMOTE_SERVER, newValue));
+
+ return remotePreferences;
}
@Override
public ProxyPreferences getProxyPreferences() {
- return new ProxyPreferences(
+ if (Objects.nonNull(proxyPreferences)) {
+ return proxyPreferences;
+ }
+
+ proxyPreferences = new ProxyPreferences(
getBoolean(PROXY_USE),
get(PROXY_HOSTNAME),
get(PROXY_PORT),
getBoolean(PROXY_USE_AUTHENTICATION),
get(PROXY_USERNAME),
get(PROXY_PASSWORD));
- }
- @Override
- public void storeProxyPreferences(ProxyPreferences preferences) {
- putBoolean(PROXY_USE, preferences.isUseProxy());
- put(PROXY_HOSTNAME, preferences.getHostname());
- put(PROXY_PORT, preferences.getPort());
- putBoolean(PROXY_USE_AUTHENTICATION, preferences.isUseAuthentication());
- put(PROXY_USERNAME, preferences.getUsername());
- put(PROXY_PASSWORD, preferences.getPassword());
+ EasyBind.listen(proxyPreferences.useProxyProperty(), (obs, oldValue, newValue) -> putBoolean(PROXY_USE, newValue));
+ EasyBind.listen(proxyPreferences.hostnameProperty(), (obs, oldValue, newValue) -> put(PROXY_HOSTNAME, newValue));
+ EasyBind.listen(proxyPreferences.portProperty(), (obs, oldValue, newValue) -> put(PROXY_PORT, newValue));
+ EasyBind.listen(proxyPreferences.useAuthenticationProperty(), (obs, oldValue, newValue) -> putBoolean(PROXY_USE_AUTHENTICATION, newValue));
+ EasyBind.listen(proxyPreferences.usernameProperty(), (obs, oldValue, newValue) -> put(PROXY_USERNAME, newValue));
+ EasyBind.listen(proxyPreferences.passwordProperty(), (obs, oldValue, newValue) -> put(PROXY_PASSWORD, newValue));
+
+ return proxyPreferences;
}
//*************************************************************************************************************
@@ -2020,9 +2017,9 @@ public AppearancePreferences getAppearancePreferences() {
getTheme()
);
- EasyBind.subscribe(appearancePreferences.shouldOverrideDefaultFontSizeProperty(), newValue -> putBoolean(OVERRIDE_DEFAULT_FONT_SIZE, newValue));
- EasyBind.subscribe(appearancePreferences.mainFontSizeProperty(), newValue -> putInt(MAIN_FONT_SIZE, newValue));
- EasyBind.subscribe(appearancePreferences.themeProperty(), newValue -> put(FX_THEME, newValue.getCssPathString()));
+ EasyBind.listen(appearancePreferences.shouldOverrideDefaultFontSizeProperty(), (obs, oldValue, newValue) -> putBoolean(OVERRIDE_DEFAULT_FONT_SIZE, newValue));
+ EasyBind.listen(appearancePreferences.mainFontSizeProperty(), (obs, oldValue, newValue) -> putInt(MAIN_FONT_SIZE, newValue));
+ EasyBind.listen(appearancePreferences.themeProperty(), (obs, oldValue, newValue) -> put(FX_THEME, newValue.getCssPathString()));
return appearancePreferences;
}
@@ -2031,16 +2028,15 @@ public AppearancePreferences getAppearancePreferences() {
// File preferences
//*************************************************************************************************************
- // ToDo: Can this be disbanded?
@Override
public ImportFormatPreferences getImportFormatPreferences() {
return new ImportFormatPreferences(
getCustomImportFormats(),
- getDefaultEncoding(),
getKeywordDelimiter(),
getCitationKeyPatternPreferences(),
getFieldContentParserPreferences(),
getXmpPreferences(),
+ getDOIPreferences(),
getSpecialFieldsPreferences().isKeywordSyncEnabled());
}
@@ -2110,7 +2106,6 @@ public SavePreferences getSavePreferences() {
return new SavePreferences(
false,
null,
- this.getDefaultEncoding(),
SavePreferences.DatabaseSaveType.ALL,
true,
this.getBoolean(REFORMAT_FILE_ON_SAVE_AND_EXPORT),
@@ -2128,20 +2123,6 @@ public void storeOpenLastFilesOnStartup(boolean openLastFilesOnStartup) {
putBoolean(OPEN_LAST_EDITED, openLastFilesOnStartup);
}
- @Override
- public NewLineSeparator getNewLineSeparator() {
- return NewLineSeparator.parse(get(NEWLINE));
- }
-
- @Override
- public void storeNewLineSeparator(NewLineSeparator newLineSeparator) {
- String escapeChars = newLineSeparator.toString();
- put(NEWLINE, escapeChars);
-
- // We also have to change Globals variable as globals is not a getter, but a constant
- OS.NEWLINE = escapeChars;
- }
-
@Override
public FieldContentFormatterPreferences getFieldContentParserPreferences() {
return new FieldContentFormatterPreferences(
@@ -2184,30 +2165,34 @@ public void setWorkingDirectory(Path directory) {
@Override
public FilePreferences getFilePreferences() {
- return new FilePreferences(
+ if (Objects.nonNull(filePreferences)) {
+ return filePreferences;
+ }
+
+ filePreferences = new FilePreferences(
getUser(),
get(MAIN_FILE_DIRECTORY),
getBoolean(STORE_RELATIVE_TO_BIB),
get(IMPORT_FILENAMEPATTERN),
get(IMPORT_FILEDIRPATTERN),
- getBoolean(DOWNLOAD_LINKED_FILES),
- getBoolean(RUN_AUTOMATIC_FILE_SEARCH),
- getBoolean(ALLOW_FILE_AUTO_OPEN_BROWSE));
- }
+ getBoolean(DOWNLOAD_LINKED_FILES)
+ );
- @Override
- public void storeFilePreferences(FilePreferences preferences) {
- put(MAIN_FILE_DIRECTORY, preferences.getFileDirectory().map(Path::toString).orElse(""));
- putBoolean(STORE_RELATIVE_TO_BIB, preferences.shouldStoreFilesRelativeToBib());
- put(IMPORT_FILENAMEPATTERN, preferences.getFileNamePattern());
- put(IMPORT_FILEDIRPATTERN, preferences.getFileDirectoryPattern());
- putBoolean(DOWNLOAD_LINKED_FILES, preferences.shouldDownloadLinkedFiles());
- putBoolean(RUN_AUTOMATIC_FILE_SEARCH, preferences.shouldSearchFilesOnOpen());
- putBoolean(ALLOW_FILE_AUTO_OPEN_BROWSE, preferences.shouldOpenBrowseOnCreate());
+ EasyBind.listen(filePreferences.mainFileDirectoryProperty(), (obs, oldValue, newValue) -> put(MAIN_FILE_DIRECTORY, filePreferences.getFileDirectory().map(Path::toString).orElse("")));
+ EasyBind.listen(filePreferences.storeFilesRelativeToBibFileProperty(), (obs, oldValue, newValue) -> putBoolean(STORE_RELATIVE_TO_BIB, newValue));
+ EasyBind.listen(filePreferences.fileNamePatternProperty(), (obs, oldValue, newValue) -> put(IMPORT_FILENAMEPATTERN, newValue));
+ EasyBind.listen(filePreferences.fileDirectoryPatternProperty(), (obs, oldValue, newValue) -> put(IMPORT_FILEDIRPATTERN, newValue));
+ EasyBind.listen(filePreferences.downloadLinkedFilesProperty(), (obs, oldValue, newValue) -> putBoolean(DOWNLOAD_LINKED_FILES, newValue));
+
+ return filePreferences;
}
@Override
public AutoLinkPreferences getAutoLinkPreferences() {
+ if (Objects.nonNull(autoLinkPreferences)) {
+ return autoLinkPreferences;
+ }
+
AutoLinkPreferences.CitationKeyDependency citationKeyDependency =
AutoLinkPreferences.CitationKeyDependency.START; // default
if (getBoolean(AUTOLINK_EXACT_KEY_ONLY)) {
@@ -2216,32 +2201,33 @@ public AutoLinkPreferences getAutoLinkPreferences() {
citationKeyDependency = AutoLinkPreferences.CitationKeyDependency.REGEX;
}
- return new AutoLinkPreferences(
+ autoLinkPreferences = new AutoLinkPreferences(
citationKeyDependency,
get(AUTOLINK_REG_EXP_SEARCH_EXPRESSION_KEY),
getBoolean(ASK_AUTO_NAMING_PDFS_AGAIN),
getKeywordDelimiter());
- }
- @Override
- public void storeAutoLinkPreferences(AutoLinkPreferences preferences) {
- // Starts bibtex only omitted, as it is not being saved
- switch (preferences.getCitationKeyDependency()) {
- case START -> {
- putBoolean(AUTOLINK_EXACT_KEY_ONLY, false);
- putBoolean(AUTOLINK_USE_REG_EXP_SEARCH_KEY, false);
- }
- case EXACT -> {
- putBoolean(AUTOLINK_EXACT_KEY_ONLY, true);
- putBoolean(AUTOLINK_USE_REG_EXP_SEARCH_KEY, false);
- }
- case REGEX -> {
- putBoolean(AUTOLINK_EXACT_KEY_ONLY, false);
- putBoolean(AUTOLINK_USE_REG_EXP_SEARCH_KEY, true);
- }
- }
- putBoolean(ASK_AUTO_NAMING_PDFS_AGAIN, preferences.shouldAskAutoNamingPdfs());
- put(AUTOLINK_REG_EXP_SEARCH_EXPRESSION_KEY, preferences.getRegularExpression());
+ EasyBind.listen(autoLinkPreferences.citationKeyDependencyProperty(), (obs, oldValue, newValue) -> {
+ // Starts bibtex only omitted, as it is not being saved
+ switch (newValue) {
+ case START -> {
+ putBoolean(AUTOLINK_EXACT_KEY_ONLY, false);
+ putBoolean(AUTOLINK_USE_REG_EXP_SEARCH_KEY, false);
+ }
+ case EXACT -> {
+ putBoolean(AUTOLINK_EXACT_KEY_ONLY, true);
+ putBoolean(AUTOLINK_USE_REG_EXP_SEARCH_KEY, false);
+ }
+ case REGEX -> {
+ putBoolean(AUTOLINK_EXACT_KEY_ONLY, false);
+ putBoolean(AUTOLINK_USE_REG_EXP_SEARCH_KEY, true);
+ }
+ }
+ });
+ EasyBind.listen(autoLinkPreferences.askAutoNamingPdfsProperty(), (obs, oldValue, newValue) -> putBoolean(ASK_AUTO_NAMING_PDFS_AGAIN, newValue));
+ EasyBind.listen(autoLinkPreferences.regularExpressionProperty(), (obs, oldValue, newValue) -> put(AUTOLINK_REG_EXP_SEARCH_EXPRESSION_KEY, newValue));
+
+ return autoLinkPreferences;
}
@Override
@@ -2260,28 +2246,30 @@ public void storeShouldAutosave(boolean shouldAutosave) {
@Override
public ImportExportPreferences getImportExportPreferences() {
- return new ImportExportPreferences(
+ if (Objects.nonNull(importExportPreferences)) {
+ return importExportPreferences;
+ }
+
+ importExportPreferences = new ImportExportPreferences(
get(NON_WRAPPABLE_FIELDS),
!getBoolean(RESOLVE_STRINGS_ALL_FIELDS),
getBoolean(RESOLVE_STRINGS_ALL_FIELDS),
get(DO_NOT_RESOLVE_STRINGS_FOR),
- getNewLineSeparator(),
getBoolean(REFORMAT_FILE_ON_SAVE_AND_EXPORT),
Path.of(get(IMPORT_WORKING_DIRECTORY)),
get(LAST_USED_EXPORT),
Path.of(get(EXPORT_WORKING_DIRECTORY)));
- }
- @Override
- public void storeImportExportPreferences(ImportExportPreferences preferences) {
- put(NON_WRAPPABLE_FIELDS, preferences.getNonWrappableFields());
- putBoolean(RESOLVE_STRINGS_ALL_FIELDS, preferences.shouldResolveStringsForAllStrings());
- put(DO_NOT_RESOLVE_STRINGS_FOR, preferences.getNonResolvableFields());
- storeNewLineSeparator(preferences.getNewLineSeparator());
- putBoolean(REFORMAT_FILE_ON_SAVE_AND_EXPORT, preferences.shouldAlwaysReformatOnSave());
- put(IMPORT_WORKING_DIRECTORY, preferences.getImportWorkingDirectory().toString());
- put(LAST_USED_EXPORT, preferences.getLastExportExtension());
- put(EXPORT_WORKING_DIRECTORY, preferences.getExportWorkingDirectory().toString());
+ EasyBind.listen(importExportPreferences.nonWrappableFieldsProperty(), (obs, oldValue, newValue) -> put(NON_WRAPPABLE_FIELDS, newValue));
+ EasyBind.listen(importExportPreferences.resolveStringsForStandardBibtexFieldsProperty(), (obs, oldValue, newValue) -> putBoolean(RESOLVE_STRINGS_ALL_FIELDS, newValue));
+ EasyBind.listen(importExportPreferences.resolveStringsForAllStringsProperty(), (obs, oldValue, newValue) -> putBoolean(RESOLVE_STRINGS_ALL_FIELDS, newValue));
+ EasyBind.listen(importExportPreferences.nonResolvableFieldsProperty(), (obs, oldValue, newValue) -> put(DO_NOT_RESOLVE_STRINGS_FOR, newValue));
+ EasyBind.listen(importExportPreferences.alwaysReformatOnSaveProperty(), (obs, oldValue, newValue) -> putBoolean(REFORMAT_FILE_ON_SAVE_AND_EXPORT, newValue));
+ EasyBind.listen(importExportPreferences.importWorkingDirectoryProperty(), (obs, oldValue, newValue) -> put(IMPORT_WORKING_DIRECTORY, newValue.toString()));
+ EasyBind.listen(importExportPreferences.lastExportExtensionProperty(), (obs, oldValue, newValue) -> put(LAST_USED_EXPORT, newValue));
+ EasyBind.listen(importExportPreferences.exportWorkingDirectoryProperty(), (obs, oldValue, newValue) -> put(EXPORT_WORKING_DIRECTORY, newValue.toString()));
+
+ return importExportPreferences;
}
@Override
@@ -2454,28 +2442,42 @@ public void storePreviewPreferences(PreviewPreferences preferences) {
@Override
public SidePanePreferences getSidePanePreferences() {
- if (this.sidePanePreferences == null) {
- updateSidePanePreferences();
+ if (Objects.nonNull(sidePanePreferences)) {
+ return sidePanePreferences;
}
- return this.sidePanePreferences;
- }
- void updateSidePanePreferences() {
- this.sidePanePreferences = new SidePanePreferences(
- getBoolean(WEB_SEARCH_VISIBLE),
- getBoolean(GROUP_SIDEPANE_VISIBLE),
+ sidePanePreferences = new SidePanePreferences(
+ getVisiblePanes(),
getSidePanePreferredPositions(),
getInt(SELECTED_FETCHER_INDEX));
+
+ sidePanePreferences.visiblePanes().addListener((InvalidationListener) listener ->
+ storeVisiblePanes(sidePanePreferences.visiblePanes()));
+ sidePanePreferences.getPreferredPositions().addListener((InvalidationListener) listener ->
+ storeSidePanePreferredPositions(sidePanePreferences.getPreferredPositions()));
+ EasyBind.listen(sidePanePreferences.webSearchFetcherSelectedProperty(), (obs, oldValue, newValue) -> putInt(SELECTED_FETCHER_INDEX, newValue));
+
+ return sidePanePreferences;
}
- @Override
- public void storeSidePanePreferences(SidePanePreferences preferences) {
- putBoolean(WEB_SEARCH_VISIBLE, preferences.isWebSearchPaneVisible());
- putBoolean(GROUP_SIDEPANE_VISIBLE, preferences.isGroupsPaneVisible());
- storeSidePanePreferredPositions(preferences.getPreferredPositions());
- putInt(SELECTED_FETCHER_INDEX, preferences.getWebSearchFetcherSelected());
+ private Set getVisiblePanes() {
+ HashSet visiblePanes = new HashSet<>();
+ if (getBoolean(WEB_SEARCH_VISIBLE)) {
+ visiblePanes.add(SidePaneType.WEB_SEARCH);
+ }
+ if (getBoolean(GROUP_SIDEPANE_VISIBLE)) {
+ visiblePanes.add(SidePaneType.GROUPS);
+ }
+ if (getBoolean(OO_SHOW_PANEL)) {
+ visiblePanes.add(SidePaneType.OPEN_OFFICE);
+ }
+ return visiblePanes;
+ }
- this.sidePanePreferences = preferences;
+ private void storeVisiblePanes(Set