languages = dcSchema.getLanguages();
+ if (languages != null && !languages.isEmpty()) {
+ languages.forEach(language -> builder.append(",").append(language));
+ bibEntry.setField(StandardField.LANGUAGE, builder.substring(1));
+ }
+ }
+
/**
* Helper function for retrieving a BibEntry from the DublinCore metadata in a PDF file.
*
@@ -252,7 +265,7 @@ public Optional extractBibtexEntry() {
// then extract all "standard" dublin core entries
this.extractEditor();
this.extractAuthor();
- this.extractYearAndMonth();
+ this.extractDate();
this.extractAbstract();
this.extractDOI();
this.extractPublisher();
@@ -261,6 +274,8 @@ public Optional extractBibtexEntry() {
this.extractSubject();
this.extractTitle();
this.extractType();
+ this.extractCoverage();
+ this.extractLanguages();
// we pass a new BibEntry in the constructor which is never empty as it already consists of "@misc"
if (bibEntry.getFieldMap().isEmpty()) {
@@ -350,6 +365,37 @@ private void fillTitle(String title) {
dcSchema.setTitle(title);
}
+ /**
+ * BibTex : Coverage (Custom Field); DC Field : Coverage
+ *
+ * @param coverage
+ */
+ private void fillCoverage(String coverage) {
+ dcSchema.setCoverage(coverage);
+ }
+
+ /**
+ * BibTex Field : language ; DC Field : dc:language
+ */
+ private void fillLanguages(String languages) {
+ Arrays.stream(languages.split(","))
+ .forEach(dcSchema::addLanguage);
+ }
+
+ /**
+ * BibTex : Rights (Custom Field); DC Field : dc:rights
+ */
+ private void fillRights(String rights) {
+ dcSchema.addRights(null, rights.split(",")[0]);
+ }
+
+ /**
+ * BibTex : Source (Custom Field); DC Field : Source
+ */
+ private void fillSource(String source) {
+ dcSchema.setSource(source);
+ }
+
/**
* All others (+ citation key) get packaged in the relation attribute
*
@@ -366,29 +412,60 @@ public void fillDublinCoreSchema() {
Set> fieldValues = new TreeSet<>(Comparator.comparing(fieldStringEntry -> fieldStringEntry.getKey().getName()));
fieldValues.addAll(bibEntry.getFieldMap().entrySet());
+ boolean hasStandardYearField = fieldValues.stream().anyMatch(field -> StandardField.YEAR.equals(field.getKey()));
for (Entry field : fieldValues) {
if (useXmpPrivacyFilter && xmpPreferences.getXmpPrivacyFilter().contains(field.getKey())) {
continue;
}
- if (StandardField.EDITOR.equals(field.getKey())) {
- this.fillContributor(field.getValue());
- } else if (StandardField.AUTHOR.equals(field.getKey())) {
- this.fillCreator(field.getValue());
- } else if (StandardField.YEAR.equals(field.getKey())) {
- this.fillDate();
- } else if (StandardField.ABSTRACT.equals(field.getKey())) {
- this.fillDescription(field.getValue());
- } else if (StandardField.DOI.equals(field.getKey())) {
- this.fillIdentifier(field.getValue());
- } else if (StandardField.PUBLISHER.equals(field.getKey())) {
- this.fillPublisher(field.getValue());
- } else if (StandardField.KEYWORDS.equals(field.getKey())) {
- this.fillKeywords(field.getValue());
- } else if (StandardField.TITLE.equals(field.getKey())) {
- this.fillTitle(field.getValue());
+ Field fieldEntry = field.getKey();
+ if (fieldEntry instanceof StandardField) {
+ switch ((StandardField) fieldEntry) {
+ case EDITOR:
+ this.fillContributor(field.getValue());
+ break;
+ case AUTHOR:
+ this.fillCreator(field.getValue());
+ break;
+ case YEAR:
+ this.fillDate();
+ break;
+ case ABSTRACT:
+ this.fillDescription(field.getValue());
+ break;
+ case DOI:
+ this.fillIdentifier(field.getValue());
+ break;
+ case PUBLISHER:
+ this.fillPublisher(field.getValue());
+ break;
+ case KEYWORDS:
+ this.fillKeywords(field.getValue());
+ break;
+ case TITLE:
+ this.fillTitle(field.getValue());
+ break;
+ case LANGUAGE:
+ this.fillLanguages(field.getValue());
+ break;
+ case DAY:
+ case MONTH:
+ if (hasStandardYearField) {
+ break;
+ }
+ default:
+ this.fillCustomField(field.getKey(), field.getValue());
+ }
} else {
- this.fillCustomField(field.getKey(), field.getValue());
+ if (DC_COVERAGE.equals(fieldEntry.getName())) {
+ this.fillCoverage(field.getValue());
+ } else if (DC_RIGHTS.equals(fieldEntry.getName())) {
+ this.fillRights(field.getValue());
+ } else if (DC_SOURCE.equals(fieldEntry.getName())) {
+ this.fillSource(field.getValue());
+ } else {
+ this.fillCustomField(field.getKey(), field.getValue());
+ }
}
}
diff --git a/src/main/java/org/jabref/logic/xmp/XmpUtilReader.java b/src/main/java/org/jabref/logic/xmp/XmpUtilReader.java
index 21be8d96000..51248543d94 100644
--- a/src/main/java/org/jabref/logic/xmp/XmpUtilReader.java
+++ b/src/main/java/org/jabref/logic/xmp/XmpUtilReader.java
@@ -10,6 +10,7 @@
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.LinkedFile;
+import org.jabref.model.schema.DublinCoreSchemaCustom;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -77,8 +78,8 @@ public static List readXmp(Path path, XmpPreferences xmpPreferences)
if (!xmpMetaList.isEmpty()) {
// Only support Dublin Core since JabRef 4.2
for (XMPMetadata xmpMeta : xmpMetaList) {
- DublinCoreSchema dcSchema = xmpMeta.getDublinCoreSchema();
+ DublinCoreSchema dcSchema = DublinCoreSchemaCustom.copyDublinCoreSchema(xmpMeta.getDublinCoreSchema());
if (dcSchema != null) {
DublinCoreExtractor dcExtractor = new DublinCoreExtractor(dcSchema, xmpPreferences, new BibEntry());
Optional entry = dcExtractor.extractBibtexEntry();
diff --git a/src/main/java/org/jabref/logic/xmp/XmpUtilWriter.java b/src/main/java/org/jabref/logic/xmp/XmpUtilWriter.java
index 7133d3773b6..60fbc825b5b 100644
--- a/src/main/java/org/jabref/logic/xmp/XmpUtilWriter.java
+++ b/src/main/java/org/jabref/logic/xmp/XmpUtilWriter.java
@@ -19,6 +19,7 @@
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.StandardField;
+import org.jabref.model.schema.DublinCoreSchemaCustom;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -160,7 +161,7 @@ private static void writeDublinCore(PDDocument document,
meta.removeSchema(meta.getDublinCoreSchema());
for (BibEntry entry : resolvedEntries) {
- DublinCoreSchema dcSchema = meta.createAndAddDublinCoreSchema();
+ DublinCoreSchema dcSchema = DublinCoreSchemaCustom.copyDublinCoreSchema(meta.createAndAddDublinCoreSchema());
XmpUtilWriter.writeToDCSchema(dcSchema, entry, null, xmpPreferences);
}
diff --git a/src/main/java/org/jabref/model/entry/Date.java b/src/main/java/org/jabref/model/entry/Date.java
index b63b78c7781..a5266eb7521 100644
--- a/src/main/java/org/jabref/model/entry/Date.java
+++ b/src/main/java/org/jabref/model/entry/Date.java
@@ -20,18 +20,19 @@ public class Date {
private static final DateTimeFormatter SIMPLE_DATE_FORMATS;
static {
List formatStrings = Arrays.asList(
- "uuuu-M-d", // covers 2009-1-15
- "uuuu-M", // covers 2009-11
- "d-M-uuuu", // covers 15-1-2012
- "M-uuuu", // covers 1-2012
- "M/uuuu", // covers 9/2015 and 09/2015
- "M/uu", // covers 9/15
- "MMMM d, uuuu", // covers September 1, 2015
- "MMMM, uuuu", // covers September, 2015
- "d.M.uuuu", // covers 15.1.2015
- "uuuu.M.d", // covers 2015.1.15
- "uuuu", // covers 2015
- "MMM, uuuu"); // covers Jan, 2020
+ "uuuu-MM-dd'T'HH:mm:ss[xxx][xx][X]", // covers 2018-10-03T07:24:14+03:00
+ "uuuu-M-d", // covers 2009-1-15
+ "uuuu-M", // covers 2009-11
+ "d-M-uuuu", // covers 15-1-2012
+ "M-uuuu", // covers 1-2012
+ "M/uuuu", // covers 9/2015 and 09/2015
+ "M/uu", // covers 9/15
+ "MMMM d, uuuu", // covers September 1, 2015
+ "MMMM, uuuu", // covers September, 2015
+ "d.M.uuuu", // covers 15.1.2015
+ "uuuu.M.d", // covers 2015.1.15
+ "uuuu", // covers 2015
+ "MMM, uuuu"); // covers Jan, 2020
SIMPLE_DATE_FORMATS = formatStrings.stream()
.map(DateTimeFormatter::ofPattern)
diff --git a/src/main/java/org/jabref/model/schema/DublinCoreSchemaCustom.java b/src/main/java/org/jabref/model/schema/DublinCoreSchemaCustom.java
new file mode 100644
index 00000000000..e4d4cfe3215
--- /dev/null
+++ b/src/main/java/org/jabref/model/schema/DublinCoreSchemaCustom.java
@@ -0,0 +1,66 @@
+package org.jabref.model.schema;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.xmpbox.XMPMetadata;
+import org.apache.xmpbox.schema.DublinCoreSchema;
+import org.apache.xmpbox.type.AbstractField;
+import org.apache.xmpbox.type.ArrayProperty;
+import org.apache.xmpbox.type.DateType;
+import org.apache.xmpbox.type.StructuredType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A DublinCoreSchema extension Class.
+ * In case anyone intends to alter standard behaviour.
+ */
+@StructuredType(preferedPrefix = "dc", namespace = "http://purl.org/dc/elements/1.1/")
+public class DublinCoreSchemaCustom extends DublinCoreSchema {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DublinCoreSchemaCustom.class);
+
+ public DublinCoreSchemaCustom(XMPMetadata metadata) {
+ super(metadata);
+ }
+
+ public static DublinCoreSchema copyDublinCoreSchema(DublinCoreSchema dcSchema) {
+ if (Objects.isNull(dcSchema)) {
+ return null;
+ }
+
+ try {
+ DublinCoreSchemaCustom dublinCoreSchemaCustom = new DublinCoreSchemaCustom(dcSchema.getMetadata());
+ FieldUtils.writeField(dublinCoreSchemaCustom, "container", dcSchema.getContainer(), true);
+ FieldUtils.writeField(dublinCoreSchemaCustom, "attributes",
+ FieldUtils.readField(dcSchema, "attributes", true), true);
+ return dublinCoreSchemaCustom;
+ } catch (Exception e) {
+ LOGGER.error("Error making custom DC Schema. Using the default", e);
+ return dcSchema;
+ }
+ }
+
+ /**
+ * Overloaded XMP Schema method
+ * Behaviour is same except when seqName is "Date". Will return raw value instead
+ */
+ @Override
+ public List getUnqualifiedSequenceValueList(String seqName) {
+ AbstractField abstractProperty = getAbstractProperty(seqName);
+ if (abstractProperty instanceof ArrayProperty) {
+ if ("date".equals(seqName)) {
+ return ((ArrayProperty) abstractProperty).getContainer()
+ .getAllProperties()
+ .stream()
+ .map(field -> (String) ((DateType) field).getRawValue())
+ .collect(Collectors.toList());
+ }
+ return ((ArrayProperty) abstractProperty).getElementsAsString();
+ }
+ return null;
+ }
+}
diff --git a/src/test/java/org/jabref/logic/xmp/XmpUtilReaderTest.java b/src/test/java/org/jabref/logic/xmp/XmpUtilReaderTest.java
index 8f012187d37..fe93d9f0504 100644
--- a/src/test/java/org/jabref/logic/xmp/XmpUtilReaderTest.java
+++ b/src/test/java/org/jabref/logic/xmp/XmpUtilReaderTest.java
@@ -10,11 +10,11 @@
import java.util.Optional;
import org.jabref.logic.importer.ImportFormatPreferences;
-import org.jabref.logic.importer.ParseException;
import org.jabref.logic.importer.fileformat.BibtexImporter;
import org.jabref.logic.importer.fileformat.BibtexParser;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.LinkedFile;
+import org.jabref.model.schema.DublinCoreSchemaCustom;
import org.jabref.model.util.DummyFileUpdateMonitor;
import org.apache.xmpbox.XMPMetadata;
@@ -53,15 +53,15 @@ void setUp() {
* Tests reading of dublinCore metadata.
*/
@Test
- void testReadArticleDublinCoreReadRawXmp() throws IOException, URISyntaxException, ParseException {
- Path path = Path.of(XmpUtilShared.class.getResource("article_dublinCore.pdf").toURI());
+ void testReadArticleDublinCoreReadRawXmp() throws IOException, URISyntaxException {
+ Path path = Path.of(XmpUtilShared.class.getResource("article_dublinCore_without_day.pdf").toURI());
List meta = XmpUtilReader.readRawXmp(path);
- DublinCoreSchema dcSchema = meta.get(0).getDublinCoreSchema();
+ DublinCoreSchema dcSchema = DublinCoreSchemaCustom.copyDublinCoreSchema(meta.get(0).getDublinCoreSchema());
DublinCoreExtractor dcExtractor = new DublinCoreExtractor(dcSchema, xmpPreferences, new BibEntry());
Optional entry = dcExtractor.extractBibtexEntry();
- Path bibFile = Path.of(XmpUtilShared.class.getResource("article_dublinCore.bib").toURI());
+ Path bibFile = Path.of(XmpUtilShared.class.getResource("article_dublinCore_without_day.bib").toURI());
List expected = testImporter.importDatabase(bibFile, StandardCharsets.UTF_8).getDatabase().getEntries();
assertEquals(expected, Collections.singletonList(entry.get()));
@@ -85,6 +85,20 @@ void testReadArticleDublinCoreReadXmp() throws IOException, URISyntaxException {
assertEquals(expected, entries);
}
+ @Test
+ void testReadArticleDublinCoreReadXmpPartialDate() throws IOException, URISyntaxException {
+ Path pathPdf = Path.of(XmpUtilShared.class.getResource("article_dublinCore_partial_date.pdf").toURI());
+ List entries = XmpUtilReader.readXmp(pathPdf, xmpPreferences);
+ Path bibFile = Path.of(XmpUtilShared.class.getResource("article_dublinCore_partial_date.bib").toURI());
+ List expected = testImporter.importDatabase(bibFile, StandardCharsets.UTF_8).getDatabase().getEntries();
+
+ expected.forEach(bibEntry -> bibEntry.setFiles(Arrays.asList(
+ new LinkedFile("", pathPdf.toAbsolutePath(), "PDF"))
+ ));
+
+ assertEquals(expected, entries);
+ }
+
/**
* Tests an pdf file with an empty metadata section.
*/
diff --git a/src/test/java/org/jabref/logic/xmp/XmpUtilWriterTest.java b/src/test/java/org/jabref/logic/xmp/XmpUtilWriterTest.java
index 6013264bea4..537e0d08a42 100644
--- a/src/test/java/org/jabref/logic/xmp/XmpUtilWriterTest.java
+++ b/src/test/java/org/jabref/logic/xmp/XmpUtilWriterTest.java
@@ -8,8 +8,10 @@
import javax.xml.transform.TransformerException;
import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.Date;
import org.jabref.model.entry.Month;
import org.jabref.model.entry.field.StandardField;
+import org.jabref.model.entry.field.UnknownField;
import org.jabref.model.entry.types.StandardEntryType;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -18,6 +20,9 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import static org.jabref.logic.xmp.DublinCoreExtractor.DC_COVERAGE;
+import static org.jabref.logic.xmp.DublinCoreExtractor.DC_RIGHTS;
+import static org.jabref.logic.xmp.DublinCoreExtractor.DC_SOURCE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -68,6 +73,12 @@ private void initBibEntries() {
vapnik2000.setField(StandardField.AUTHOR, "Vladimir N. Vapnik");
vapnik2000.setField(StandardField.DOI, "10.1007/978-1-4757-3264-1");
vapnik2000.setField(StandardField.OWNER, "Ich");
+ vapnik2000.setField(StandardField.LANGUAGE, "English, Japanese");
+ vapnik2000.setDate(new Date(2000, 5));
+
+ vapnik2000.setField(new UnknownField(DC_COVERAGE), "coverageField");
+ vapnik2000.setField(new UnknownField((DC_SOURCE)), "JabRef");
+ vapnik2000.setField(new UnknownField(DC_RIGHTS), "Right To X");
}
/**
diff --git a/src/test/resources/org/jabref/logic/xmp/article_dublinCore.bib b/src/test/resources/org/jabref/logic/xmp/article_dublinCore.bib
index 82c5604dd38..f959a333921 100644
--- a/src/test/resources/org/jabref/logic/xmp/article_dublinCore.bib
+++ b/src/test/resources/org/jabref/logic/xmp/article_dublinCore.bib
@@ -7,6 +7,7 @@ @Article{Olly2018
number = {1},
pages = {1-2},
month = mar,
+ day = {1},
issn = {978-123-123},
note = {That's a note},
abstract = {That's an abstract},
diff --git a/src/test/resources/org/jabref/logic/xmp/article_dublinCore_partial_date.bib b/src/test/resources/org/jabref/logic/xmp/article_dublinCore_partial_date.bib
new file mode 100644
index 00000000000..39c4e5d137e
--- /dev/null
+++ b/src/test/resources/org/jabref/logic/xmp/article_dublinCore_partial_date.bib
@@ -0,0 +1,8 @@
+@Article{,
+ coverage = {coverageField},
+ language = {English,Japanese},
+ month = jan,
+ rights = {Right To X},
+ source = {JabRef},
+ year = {2005},
+}
diff --git a/src/test/resources/org/jabref/logic/xmp/article_dublinCore_partial_date.pdf b/src/test/resources/org/jabref/logic/xmp/article_dublinCore_partial_date.pdf
new file mode 100644
index 00000000000..b9343488134
Binary files /dev/null and b/src/test/resources/org/jabref/logic/xmp/article_dublinCore_partial_date.pdf differ
diff --git a/src/test/resources/org/jabref/logic/xmp/article_dublinCore_without_day.bib b/src/test/resources/org/jabref/logic/xmp/article_dublinCore_without_day.bib
new file mode 100644
index 00000000000..82c5604dd38
--- /dev/null
+++ b/src/test/resources/org/jabref/logic/xmp/article_dublinCore_without_day.bib
@@ -0,0 +1,23 @@
+@Article{Olly2018,
+ author = {Olly and Johannes},
+ title = {Stefan's palace},
+ journal = {Test Journal},
+ year = {2018},
+ volume = {1},
+ number = {1},
+ pages = {1-2},
+ month = mar,
+ issn = {978-123-123},
+ note = {That's a note},
+ abstract = {That's an abstract},
+ comment = {That's a comment},
+ doi = {10/3212.3123},
+ file = {:paper.pdf:PDF},
+ groups = {NO},
+ howpublished = {Online},
+ keywords = {Keyword1, Keyword2},
+ owner = {Me},
+ review = {Here are the reviews},
+ timestamp = {2018-02-15},
+ url = {https://www.olly2018.edu},
+}
diff --git a/src/test/resources/org/jabref/logic/xmp/article_dublinCore_without_day.pdf b/src/test/resources/org/jabref/logic/xmp/article_dublinCore_without_day.pdf
new file mode 100644
index 00000000000..ae2d982b0a3
Binary files /dev/null and b/src/test/resources/org/jabref/logic/xmp/article_dublinCore_without_day.pdf differ