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 ebb1282a787..c235fe11401 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java @@ -20,6 +20,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Predicate; +import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilderFactory; @@ -58,6 +59,8 @@ import com.dd.plist.BinaryPropertyListParser; import com.dd.plist.NSDictionary; import com.dd.plist.NSString; +import com.google.gson.Gson; +import com.google.gson.JsonObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -354,6 +357,7 @@ private void parseJabRefComment(Map meta) { // We remove all line breaks in the metadata // These have been inserted to prevent too long lines when the file was saved, and are not part of the data. String comment = buffer.toString().replaceAll("[\\x0d\\x0a]", ""); + if (comment.substring(0, Math.min(comment.length(), MetaData.META_FLAG.length())).equals(MetaData.META_FLAG)) { if (comment.startsWith(MetaData.META_FLAG)) { String rest = comment.substring(MetaData.META_FLAG.length()); @@ -386,7 +390,23 @@ private void parseJabRefComment(Map meta) { } catch (ParseException ex) { parserResult.addException(ex); } + } else if (comment.substring(0, Math.min(comment.length(), MetaData.META_FLAG_VERSION_010.length())).equals(MetaData.META_FLAG_VERSION_010)) { + parseCommentToJson(comment, meta); + } + } + + private JsonObject parseCommentToJson(String comment, Map meta) { + Pattern pattern = Pattern.compile("\\{.*}", Pattern.DOTALL); + Matcher matcher = pattern.matcher(comment); + if (matcher.find()) { + String jsonString = matcher.group(); + Gson gson = new Gson(); + JsonObject jsonObject = gson.fromJson(jsonString, JsonObject.class); + String jsonResult = gson.toJson(jsonObject); + meta.putIfAbsent(MetaData.META_FLAG_VERSION_010, jsonResult); + return jsonObject; } + return null; } /** diff --git a/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java b/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java index d1e66146412..aef6c0bb3ee 100644 --- a/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java +++ b/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java @@ -4,6 +4,7 @@ import java.io.Reader; import java.io.StringReader; import java.nio.file.Path; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -35,6 +36,8 @@ import org.jabref.model.strings.StringUtil; import org.jabref.model.util.FileUpdateMonitor; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -97,6 +100,77 @@ public MetaData parse(Map data, Character keywordSeparator) thro return parse(new MetaData(), data, keywordSeparator); } + public MetaData parse(JsonObject data, Character keywordSeparator) throws ParseException { + return parse(new MetaData(), data, keywordSeparator); + } + + public MetaData parse(MetaData metaData, JsonObject data, Character keywordSeparator) throws ParseException { + CitationKeyPattern defaultCiteKeyPattern = CitationKeyPattern.NULL_CITATION_KEY_PATTERN; + Map nonDefaultCiteKeyPatterns = new HashMap<>(); + + // process groups (GROUPSTREE and GROUPSTREE_LEGACY) at the very end (otherwise it can happen that not all dependent data are set) + List> entryList = new ArrayList<>(); + for (Map.Entry entry : data.entrySet()) { + // Add each entry to the list, converting JsonElement to String + entryList.add(new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue().getAsString())); + } + + entryList.sort(groupsLast()); + + for (Map.Entry entry : entryList) { + List values = getAsList(entry.getValue()); + + if (entry.getKey().startsWith(MetaData.PREFIX_KEYPATTERN)) { + EntryType entryType = EntryTypeFactory.parse(entry.getKey().substring(MetaData.PREFIX_KEYPATTERN.length())); + nonDefaultCiteKeyPatterns.put(entryType, new CitationKeyPattern(getSingleItem(values))); + } else if (entry.getKey().startsWith(MetaData.SELECTOR_META_PREFIX)) { + // edge case, it might be one special field e.g. article from biblatex-apa, but we can't distinguish this from any other field and rather prefer to handle it as UnknownField + metaData.addContentSelector(ContentSelectors.parse(FieldFactory.parseField(entry.getKey().substring(MetaData.SELECTOR_META_PREFIX.length())), StringUtil.unquote(entry.getValue(), MetaData.ESCAPE_CHARACTER))); + } else if (entry.getKey().equals(MetaData.FILE_DIRECTORY)) { + metaData.setLibrarySpecificFileDirectory(parseDirectory(entry.getValue())); + } else if (entry.getKey().startsWith(MetaData.FILE_DIRECTORY + '-')) { + // The user name starts directly after FILE_DIRECTORY + '-' + String user = entry.getKey().substring(MetaData.FILE_DIRECTORY.length() + 1); + metaData.setUserFileDirectory(user, parseDirectory(entry.getValue())); + } else if (entry.getKey().startsWith(MetaData.FILE_DIRECTORY_LATEX)) { + // The user name starts directly after FILE_DIRECTORY_LATEX + '-' + String user = entry.getKey().substring(MetaData.FILE_DIRECTORY_LATEX.length() + 1); + Path path = Path.of(parseDirectory(entry.getValue())).normalize(); + metaData.setLatexFileDirectory(user, path); + } else if (entry.getKey().equals(MetaData.SAVE_ACTIONS)) { + metaData.setSaveActions(fieldFormatterCleanupsParse(values)); + } else if (entry.getKey().equals(MetaData.DATABASE_TYPE)) { + metaData.setMode(BibDatabaseMode.parse(getSingleItem(values))); + } else if (entry.getKey().equals(MetaData.KEYPATTERNDEFAULT)) { + defaultCiteKeyPattern = new CitationKeyPattern(getSingleItem(values)); + } else if (entry.getKey().equals(MetaData.PROTECTED_FLAG_META)) { + if (Boolean.parseBoolean(getSingleItem(values))) { + metaData.markAsProtected(); + } else { + metaData.markAsNotProtected(); + } + } else if (entry.getKey().equals(MetaData.SAVE_ORDER_CONFIG)) { + metaData.setSaveOrder(SaveOrder.parse(values)); + } else if (entry.getKey().equals(MetaData.GROUPSTREE) || entry.getKey().equals(MetaData.GROUPSTREE_LEGACY)) { + metaData.setGroups(GroupsParser.importGroups(values, keywordSeparator, fileMonitor, metaData)); + } else if (entry.getKey().equals(MetaData.GROUPS_SEARCH_SYNTAX_VERSION)) { + Version version = Version.parse(getSingleItem(values)); + metaData.setGroupSearchSyntaxVersion(version); + } else if (entry.getKey().equals(MetaData.VERSION_DB_STRUCT)) { + metaData.setVersionDBStructure(getSingleItem(values)); + } else { + // Keep meta data items that we do not know in the file + metaData.putUnknownMetaDataItem(entry.getKey(), values); + } + } + + if (!defaultCiteKeyPattern.equals(CitationKeyPattern.NULL_CITATION_KEY_PATTERN) || !nonDefaultCiteKeyPatterns.isEmpty()) { + metaData.setCiteKeyPattern(defaultCiteKeyPattern, nonDefaultCiteKeyPatterns); + } + + return metaData; + } + /** * Parses the data map and changes the given {@link MetaData} instance respectively. * diff --git a/src/main/java/org/jabref/model/metadata/MetaData.java b/src/main/java/org/jabref/model/metadata/MetaData.java index c7be01ce09b..0a251745b5b 100644 --- a/src/main/java/org/jabref/model/metadata/MetaData.java +++ b/src/main/java/org/jabref/model/metadata/MetaData.java @@ -31,13 +31,12 @@ import com.google.common.eventbus.EventBus; import com.tobiasdiez.easybind.optional.OptionalBinding; import com.tobiasdiez.easybind.optional.OptionalWrapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @AllowedToUseLogic("because it needs access to citation pattern and cleanups") public class MetaData { public static final String META_FLAG = "jabref-meta: "; + public static final String META_FLAG_VERSION_010 = "jabref-meta-0.1.0"; public static final String ENTRYTYPE_FLAG = "jabref-entrytype: "; public static final String SAVE_ORDER_CONFIG = "saveOrderConfig"; // ToDo: Rename in next major version to saveOrder, adapt testbibs public static final String SAVE_ACTIONS = "saveActions"; @@ -58,8 +57,6 @@ public class MetaData { public static final char SEPARATOR_CHARACTER = ';'; public static final String SEPARATOR_STRING = String.valueOf(SEPARATOR_CHARACTER); - private static final Logger LOGGER = LoggerFactory.getLogger(MetaData.class); - private final EventBus eventBus = new EventBus(); private final Map citeKeyPatterns = new HashMap<>(); // private final Map userFileDirectory = new HashMap<>(); // diff --git a/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java b/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java index 2243a401b81..8149d9d62eb 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java @@ -55,6 +55,7 @@ import org.jabref.model.groups.RegexKeywordGroup; import org.jabref.model.groups.TexGroup; import org.jabref.model.groups.WordKeywordGroup; +import org.jabref.model.metadata.MetaData; import org.jabref.model.metadata.SaveOrder; import org.junit.jupiter.api.BeforeEach; @@ -2238,4 +2239,26 @@ void parseInvalidBibDeskFilesResultsInWarnings() throws IOException { assertEquals(List.of(firstEntry, secondEntry), result.getDatabase().getEntries()); } + + @Test + void parseJabRefSingleJsonComment() throws IOException { + String entries = + """ + @Comment{jabref-meta-0.1.0 + { + "saveActions" : + { + "state": true, + "date": ["normalize_date", "action2"], + "pages" : ["normalize_page_numbers"], + "month" : ["normalize_month"] + } + } + } + """; + ParserResult result = parser.parse(new StringReader(entries)); + MetaData expectedMetaData = new MetaData(); + expectedMetaData.putUnknownMetaDataItem("jabref-meta-0.1.0", List.of("{\"saveActions\":{\"state\":true,\"date\":[\"normalize_date\",\"action2\"],\"pages\":[\"normalize_page_numbers\"],\"month\":[\"normalize_month\"]}}")); + assertEquals(expectedMetaData, result.getMetaData()); + } }