From ea5d56e4a7a8bcb25c2bb00dc3651c807bc9739e Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 18 Feb 2018 04:48:56 +0100 Subject: [PATCH 001/560] Fixed unicode string --- scripts/syncLang.py | 55 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 150d1bbc662..a0359f18bc8 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -14,7 +14,47 @@ URL_BASE = "https://github.com/JabRef/jabref/tree/master/src/main/resources/l10n/" +try: + # Just to make sure not to break anything, in case py3.x is not supported. + import pathlib + + class PathFinder: + """ + This class is designed to automatically locate this script's path within the repository. + Once it found it's location it can easily provide paths to other important directories and files. + + requires Python 3.4 or higher + """ + + + @staticmethod + def getScriptLocation(): + """ + :return the path this script is currently located as pathlib.Path object. + """ + return pathlib.Path(__file__) + + @staticmethod + def getJabRefMainDirectory(): + """ + Searches the script's path backwards until it finds the matching base directory. + :return the path to JabRef's base directory as pathlib.Path object. + """ + scriptLocation = PathFinder.getScriptLocation() + for parent in scriptLocation.parents: + if parent.name == 'jabref': + return parent + + # Important paths of the JabRef repository + JABREF_BASE_DIRECTORY = PathFinder.getJabRefMainDirectory() + JABREF_SOURCE_DIRECTORY = JABREF_BASE_DIRECTORY / 'src' + JABREF_SCRIPTS_DIRECTORY = JABREF_BASE_DIRECTORY / 'scripts' + JABREF_LOCALIZATION_DIRECTORY = JABREF_SOURCE_DIRECTORY / 'main/resources/l10n' +except: + logging.info("Unable to use PathFinder class.") + class Git: + def get_current_branch(self): """ :return: the current git branch @@ -36,6 +76,7 @@ def __call_command(self, command): class Keys: + def __init__(self, lines): self.lines = lines @@ -50,7 +91,7 @@ def duplicates(self): if key: if key in keys_checked: duplicates.append(u"{key}={value}".format(key=key, value=value)) - translation_in_list = "u{key}={value}".format(key=key, value=keys_checked[key]) + translation_in_list = u"{key}={value}".format(key=key, value=keys_checked[key]) if translation_in_list not in duplicates: duplicates.append(translation_in_list) else: @@ -152,6 +193,7 @@ def __extract_key_and_value(line): class SyncLang: + def __init__(self, extended, out_file='status.md'): """ :param extended: boolean: if the keys with problems should be printed @@ -258,7 +300,7 @@ def __all_jabref_properties(self): :return: list of strings: all the JabRef_*.preferences files with the english at the beginning """ jabref_property_files = sorted(self.__other_jabref_properties()) - jabref_property_files.insert(0, os.path.join(RES_DIR, "JabRef_en.properties")) + jabref_property_files.insert(0, self.main_jabref_preferences) return jabref_property_files def __other_jabref_properties(self): @@ -315,7 +357,7 @@ def __update_properties(self, main_property_file, other_property_files): key = main_keys.key_from_line(line) if key is not None: # Do not write empty keys - if keys[key] != "": + if keys[key] != "": other_lines_to_write.append(u"{key}={value}\n".format(key=key, value=keys[key])) else: other_lines_to_write.append(line) @@ -433,8 +475,7 @@ def _percentage(whole, part): logging.info('Current status written to ' + self.markdown_output) -if '__main__' == __name__: - +def main(): if len(sys.argv) == 2 and sys.argv[1] == "markdown": SyncLang(extended=False, out_file='status.md').status_create_markdown() @@ -464,3 +505,7 @@ def _percentage(whole, part): [-e | --extended]: if the translations keys which create problems should be printed """) + + +if '__main__' == __name__: + main() From ba7d87d2415c54992952b89cdc4673194a5ee929 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 18 Feb 2018 05:55:53 +0100 Subject: [PATCH 002/560] Fixed logical issue within merging process --- scripts/syncLang.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index a0359f18bc8..33305f9c01b 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -111,15 +111,15 @@ def fix_duplicates(self): if key: if key in keys: if not keys[key]: - fixed.append("{key}={value}".format(key=key, value=keys[key])) + fixed.append(u"{key}={value}".format(key=key, value=keys[key])) keys[key] = value elif not value: - fixed.append("{key}={value}".format(key=key, value=value)) + fixed.append(u"{key}={value}".format(key=key, value=value)) elif keys[key] == value: - fixed.append("{key}={value}".format(key=key, value=value)) + fixed.append(u"{key}={value}".format(key=key, value=value)) elif keys[key] != value: - not_fixed.append("{key}={value}".format(key=key, value=value)) - not_fixed.append("{key}={value}".format(key=key, value=keys[key])) + not_fixed.append(u"{key}={value}".format(key=key, value=value)) + not_fixed.append(u"{key}={value}".format(key=key, value=keys[key])) else: keys[key] = value @@ -347,9 +347,11 @@ def __update_properties(self, main_property_file, other_property_files): num_keys_obsolete = len(keys_obsolete) for missing_key in keys_missing: + logging.debug("Adding missing Key: " + missing_key) keys[missing_key] = "" for obsolete_key in keys_obsolete: + logging.debug("Deleting obsolete Key: " + obsolete_key) del keys[obsolete_key] other_lines_to_write = [] @@ -357,8 +359,8 @@ def __update_properties(self, main_property_file, other_property_files): key = main_keys.key_from_line(line) if key is not None: # Do not write empty keys - if keys[key] != "": - other_lines_to_write.append(u"{key}={value}\n".format(key=key, value=keys[key])) + #if keys[key] != "": + other_lines_to_write.append(u"{key}={value}\n".format(key=key, value=keys[key])) else: other_lines_to_write.append(line) From 254449fea3e712ece044322c7a73adc547014cba Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 18 Feb 2018 13:22:44 +0100 Subject: [PATCH 003/560] Added format_key_and_value method --- scripts/syncLang.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 33305f9c01b..11c870460e8 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -13,7 +13,6 @@ RES_DIR = "src/main/resources/l10n" URL_BASE = "https://github.com/JabRef/jabref/tree/master/src/main/resources/l10n/" - try: # Just to make sure not to break anything, in case py3.x is not supported. import pathlib @@ -26,7 +25,6 @@ class PathFinder: requires Python 3.4 or higher """ - @staticmethod def getScriptLocation(): """ @@ -52,6 +50,7 @@ def getJabRefMainDirectory(): JABREF_LOCALIZATION_DIRECTORY = JABREF_SOURCE_DIRECTORY / 'main/resources/l10n' except: logging.info("Unable to use PathFinder class.") + class Git: @@ -90,8 +89,8 @@ def duplicates(self): key, value = self.__extract_key_and_value(line=line) if key: if key in keys_checked: - duplicates.append(u"{key}={value}".format(key=key, value=value)) - translation_in_list = u"{key}={value}".format(key=key, value=keys_checked[key]) + duplicates.append(self.format_key_and_value(key=key, value=value)) + translation_in_list = self.format_key_and_value(key=key, value=keys_checked[key]) if translation_in_list not in duplicates: duplicates.append(translation_in_list) else: @@ -111,15 +110,15 @@ def fix_duplicates(self): if key: if key in keys: if not keys[key]: - fixed.append(u"{key}={value}".format(key=key, value=keys[key])) + fixed.append(self.format_key_and_value(key=key, value=keys[key])) keys[key] = value elif not value: - fixed.append(u"{key}={value}".format(key=key, value=value)) + fixed.append(self.format_key_and_value(key=key, value=value)) elif keys[key] == value: - fixed.append(u"{key}={value}".format(key=key, value=value)) + fixed.append(self.format_key_and_value(key=key, value=value)) elif keys[key] != value: - not_fixed.append(u"{key}={value}".format(key=key, value=value)) - not_fixed.append(u"{key}={value}".format(key=key, value=keys[key])) + not_fixed.append(self.format_key_and_value(key=key, value=value)) + not_fixed.append(self.format_key_and_value(key=key, value=keys[key])) else: keys[key] = value @@ -190,6 +189,10 @@ def __extract_key_and_value(line): if index_key_end > 0: return line[0:index_key_end].strip(), line[index_key_end + 1:].strip() return None, None + + @staticmethod + def format_key_and_value(key, value): + return u"{key}={value}".format(key=key, value=value) class SyncLang: @@ -359,8 +362,8 @@ def __update_properties(self, main_property_file, other_property_files): key = main_keys.key_from_line(line) if key is not None: # Do not write empty keys - #if keys[key] != "": - other_lines_to_write.append(u"{key}={value}\n".format(key=key, value=keys[key])) + # if keys[key] != "": + other_lines_to_write.append(Keys.format_key_and_value(key=key, value=keys[key])) else: other_lines_to_write.append(line) From b00bfdd6ce8b3373fa58f6757edee04f79426fe0 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 18 Feb 2018 13:40:13 +0100 Subject: [PATCH 004/560] Added new line for new properties --- scripts/syncLang.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 11c870460e8..c9fa5dc70d6 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -363,7 +363,7 @@ def __update_properties(self, main_property_file, other_property_files): if key is not None: # Do not write empty keys # if keys[key] != "": - other_lines_to_write.append(Keys.format_key_and_value(key=key, value=keys[key])) + other_lines_to_write.append(Keys.format_key_and_value(key=key, value=keys[key]) + "\n") else: other_lines_to_write.append(line) From 7a7f33f27a6e38737869b5756df1dfa4d8f26ee3 Mon Sep 17 00:00:00 2001 From: Stefan Date: Sun, 18 Feb 2018 15:40:47 +0100 Subject: [PATCH 005/560] Renamed method and moved Path variables into PathFinder class --- scripts/syncLang.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index c9fa5dc70d6..52a5fdf062f 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -33,7 +33,7 @@ def getScriptLocation(): return pathlib.Path(__file__) @staticmethod - def getJabRefMainDirectory(): + def getJabRefBaseDirectory(): """ Searches the script's path backwards until it finds the matching base directory. :return the path to JabRef's base directory as pathlib.Path object. @@ -43,11 +43,11 @@ def getJabRefMainDirectory(): if parent.name == 'jabref': return parent - # Important paths of the JabRef repository - JABREF_BASE_DIRECTORY = PathFinder.getJabRefMainDirectory() - JABREF_SOURCE_DIRECTORY = JABREF_BASE_DIRECTORY / 'src' - JABREF_SCRIPTS_DIRECTORY = JABREF_BASE_DIRECTORY / 'scripts' - JABREF_LOCALIZATION_DIRECTORY = JABREF_SOURCE_DIRECTORY / 'main/resources/l10n' + # Important paths of the JabRef repository + JABREF_BASE_DIRECTORY = self.getJabRefBaseDirectory() + JABREF_SOURCE_DIRECTORY = JABREF_BASE_DIRECTORY / 'src' + JABREF_SCRIPTS_DIRECTORY = JABREF_BASE_DIRECTORY / 'scripts' + JABREF_LOCALIZATION_DIRECTORY = JABREF_SOURCE_DIRECTORY / 'main/resources/l10n' except: logging.info("Unable to use PathFinder class.") From 759ea5d4436ac3fb3321453a2aaca97630dc5cf0 Mon Sep 17 00:00:00 2001 From: Stefan Date: Mon, 19 Feb 2018 12:44:27 +0100 Subject: [PATCH 006/560] Removed empty line, except statement is now catching ImportErrors --- scripts/syncLang.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 52a5fdf062f..817dcfbed08 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -16,7 +16,6 @@ try: # Just to make sure not to break anything, in case py3.x is not supported. import pathlib - class PathFinder: """ This class is designed to automatically locate this script's path within the repository. @@ -48,7 +47,7 @@ def getJabRefBaseDirectory(): JABREF_SOURCE_DIRECTORY = JABREF_BASE_DIRECTORY / 'src' JABREF_SCRIPTS_DIRECTORY = JABREF_BASE_DIRECTORY / 'scripts' JABREF_LOCALIZATION_DIRECTORY = JABREF_SOURCE_DIRECTORY / 'main/resources/l10n' -except: +except ImportError: logging.info("Unable to use PathFinder class.") From f5752453b825c1fe4763130c736c469fcb33fa31 Mon Sep 17 00:00:00 2001 From: Stefan Date: Mon, 19 Feb 2018 22:15:55 +0100 Subject: [PATCH 007/560] Ignoring empty keys again, new keys are added in english by default. --- scripts/syncLang.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 817dcfbed08..fb612e494a9 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -315,6 +315,7 @@ def __other_jabref_properties(self): def __update_properties(self, main_property_file, other_property_files): main_lines = self.__read_file_as_lines(filename=main_property_file) main_keys = Keys(main_lines) + main_keys_dict = main_keys.translations_as_dict() main_duplicates = main_keys.duplicates() num_main_duplicates = len(main_duplicates) @@ -350,7 +351,8 @@ def __update_properties(self, main_property_file, other_property_files): for missing_key in keys_missing: logging.debug("Adding missing Key: " + missing_key) - keys[missing_key] = "" + # Missing keys are added with english translation by default. + keys[missing_key] = main_keys_dict[missing_key] for obsolete_key in keys_obsolete: logging.debug("Deleting obsolete Key: " + obsolete_key) @@ -361,8 +363,8 @@ def __update_properties(self, main_property_file, other_property_files): key = main_keys.key_from_line(line) if key is not None: # Do not write empty keys - # if keys[key] != "": - other_lines_to_write.append(Keys.format_key_and_value(key=key, value=keys[key]) + "\n") + if keys[key] != "": + other_lines_to_write.append(Keys.format_key_and_value(key=key, value=keys[key]) + "\n") else: other_lines_to_write.append(line) From 19edb0fb0bf68745f4d37a7d546e61ee54645fc7 Mon Sep 17 00:00:00 2001 From: Stefan Date: Mon, 19 Feb 2018 22:19:46 +0100 Subject: [PATCH 008/560] Removed debug messages --- scripts/syncLang.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index fb612e494a9..a4903490d5d 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -350,12 +350,10 @@ def __update_properties(self, main_property_file, other_property_files): num_keys_obsolete = len(keys_obsolete) for missing_key in keys_missing: - logging.debug("Adding missing Key: " + missing_key) # Missing keys are added with english translation by default. keys[missing_key] = main_keys_dict[missing_key] for obsolete_key in keys_obsolete: - logging.debug("Deleting obsolete Key: " + obsolete_key) del keys[obsolete_key] other_lines_to_write = [] From 011501dde1559b92cc82262612719741b56ddc00 Mon Sep 17 00:00:00 2001 From: Stefan Date: Mon, 19 Feb 2018 22:35:36 +0100 Subject: [PATCH 009/560] Had to kick out the Path variables of the class again. :/ --- scripts/syncLang.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index a4903490d5d..3f303c4ca34 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -42,11 +42,11 @@ def getJabRefBaseDirectory(): if parent.name == 'jabref': return parent - # Important paths of the JabRef repository - JABREF_BASE_DIRECTORY = self.getJabRefBaseDirectory() - JABREF_SOURCE_DIRECTORY = JABREF_BASE_DIRECTORY / 'src' - JABREF_SCRIPTS_DIRECTORY = JABREF_BASE_DIRECTORY / 'scripts' - JABREF_LOCALIZATION_DIRECTORY = JABREF_SOURCE_DIRECTORY / 'main/resources/l10n' + # Important paths, files and directories of the JabRef repository + JABREF_BASE_DIRECTORY = PathFinder.getJabRefBaseDirectory() + JABREF_SOURCE_DIRECTORY = JABREF_BASE_DIRECTORY / 'src' + JABREF_SCRIPTS_DIRECTORY = JABREF_BASE_DIRECTORY / 'scripts' + JABREF_LOCALIZATION_DIRECTORY = JABREF_SOURCE_DIRECTORY / 'main/resources/l10n' except ImportError: logging.info("Unable to use PathFinder class.") From e864d2cdca6e8fe867ee695d3ccb427d4fd5c691 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 20 Feb 2018 01:59:38 +0100 Subject: [PATCH 010/560] Improved method names --- scripts/syncLang.py | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 3f303c4ca34..adbc7f7f616 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -78,7 +78,7 @@ class Keys: def __init__(self, lines): self.lines = lines - def duplicates(self): + def find_duplicates(self): """ return: list of unicode strings """ @@ -220,7 +220,7 @@ def __print_status_menu_properties(self): def __print_status_jabref_properties(self): self.__check_properties(main_property_file=self.main_jabref_preferences, property_files=self.__all_jabref_properties()) - def update(self): + def update_properties(self): """ updates all the localization files fixing unambiguous duplicates, removing obsolete keys, adding missing keys, and sorting them @@ -236,19 +236,19 @@ def __update_jabref_properties(self): self.__update_properties(main_property_file=self.main_jabref_preferences, other_property_files=self.__other_jabref_properties()) def __check_properties(self, main_property_file, property_files): - main_lines = self.__read_file_as_lines(filename=main_property_file) + main_lines = self.__read_lines_from_file(filename=main_property_file) main_keys = Keys(main_lines) # the main property file gets compared to itself, but that is OK for file in property_files: filename = self.__format_filename(filepath=file) - lines = self.__read_file_as_lines(file) + lines = self.__read_lines_from_file(file) keys1 = Keys(main_lines) keys = keys1.keys_from_lines() keys_missing = self.__missing_keys(main_keys.keys_from_lines(), keys) keys_obsolete = self.__missing_keys(keys, main_keys.keys_from_lines()) - keys_duplicate = Keys(lines).duplicates() + keys_duplicate = Keys(lines).find_duplicates() keys_not_translated = Keys(lines=lines).empty_keys() num_keys = len(keys) @@ -313,11 +313,11 @@ def __other_jabref_properties(self): return [os.path.join(RES_DIR, file) for file in jabref_property_files] def __update_properties(self, main_property_file, other_property_files): - main_lines = self.__read_file_as_lines(filename=main_property_file) + main_lines = self.__read_lines_from_file(filename=main_property_file) main_keys = Keys(main_lines) main_keys_dict = main_keys.translations_as_dict() - main_duplicates = main_keys.duplicates() + main_duplicates = main_keys.find_duplicates() num_main_duplicates = len(main_duplicates) if num_main_duplicates != 0: logging.error("There are {num_duplicates} duplicates in {file}, please fix them manually".format(num_duplicates=num_main_duplicates, @@ -329,7 +329,7 @@ def __update_properties(self, main_property_file, other_property_files): for other_property_file in other_property_files: filename = self.__format_filename(filepath=other_property_file) - lines = self.__read_file_as_lines(filename=other_property_file) + lines = self.__read_lines_from_file(filename=other_property_file) keys, not_fixed, fixed = Keys(lines).fix_duplicates() num_keys = len(keys) @@ -408,13 +408,13 @@ def __write_file(filename, content): """ writes the lines to the file in `UTF-8` :param filename: string - :param content: list of unicode unicode: the lines to write + :param content: list of unicode strings: the lines to write """ with codecs.open(filename, 'w', encoding="UTF-8") as f: f.writelines(content) @staticmethod - def __read_file_as_lines(filename): + def __read_lines_from_file(filename): """ :param filename: string :param encoding: string: the encoding of the file to read (standard: `UTF-8`) @@ -423,7 +423,8 @@ def __read_file_as_lines(filename): with codecs.open(filename, 'r', encoding="UTF-8") as file: return [u"{}\n".format(line.strip()) for line in file.readlines()] - def __missing_keys(self, first_list, second_list): + @staticmethod + def __missing_keys(first_list, second_list): """ Finds all keys in the first list that are not present in the second list @@ -431,11 +432,7 @@ def __missing_keys(self, first_list, second_list): :param second_list: list of unicode strings :return: list of unicode strings """ - missing = [] - for key in first_list: - if key not in second_list: - missing.append(key) - return missing + return list(set(first_list).difference(second_list)) def status_create_markdown(self): """ @@ -447,7 +444,7 @@ def _write_properties(output_file, property_files): output_file.write("| ------------- | ---- | --------------- | ------------------- | ------------ |\n") for file in property_files: - lines = self.__read_file_as_lines(file) + lines = self.__read_lines_from_file(file) keys = Keys(lines) num_keys = len(keys.translations_as_dict()) num_keys_missing_value = len(keys.empty_keys()) @@ -484,7 +481,7 @@ def main(): SyncLang(extended=False, out_file='status.md').status_create_markdown() elif (len(sys.argv) == 2 or len(sys.argv) == 3) and sys.argv[1] == "update": - SyncLang(extended=len(sys.argv) == 3 and (sys.argv[2] == "-e" or sys.argv[2] == "--extended")).update() + SyncLang(extended=len(sys.argv) == 3 and (sys.argv[2] == "-e" or sys.argv[2] == "--extended")).update_properties() elif (len(sys.argv) == 2 or len(sys.argv) == 3) and sys.argv[1] == "status": SyncLang(extended=len(sys.argv) == 3 and (sys.argv[2] == "-e" or sys.argv[2] == "--extended")).status() From d940de0fcb1f4bf96a21e1bf166d56a7432a8e52 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 20 Feb 2018 21:03:36 +0100 Subject: [PATCH 011/560] Reworked command-line interface --- scripts/syncLang.py | 72 +++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index adbc7f7f616..65e75e6a7d5 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -1,6 +1,7 @@ # coding=utf-8 from __future__ import division +import argparse # Requires Python 2.7, should be safe to use with jython. import codecs import datetime import logging @@ -42,11 +43,15 @@ def getJabRefBaseDirectory(): if parent.name == 'jabref': return parent - # Important paths, files and directories of the JabRef repository + # Important directories of the JabRef repository JABREF_BASE_DIRECTORY = PathFinder.getJabRefBaseDirectory() JABREF_SOURCE_DIRECTORY = JABREF_BASE_DIRECTORY / 'src' JABREF_SCRIPTS_DIRECTORY = JABREF_BASE_DIRECTORY / 'scripts' JABREF_LOCALIZATION_DIRECTORY = JABREF_SOURCE_DIRECTORY / 'main/resources/l10n' + + # Important files + JABREF_MAIN_LOCALIZATION_FILE = JABREF_LOCALIZATION_DIRECTORY / 'JabRef_en.properties' + JABREF_MAIN_MENU_LOCALIZATION_FILE = JABREF_LOCALIZATION_DIRECTORY / 'Menu_en.properties' except ImportError: logging.info("Unable to use PathFinder class.") @@ -196,7 +201,7 @@ def format_key_and_value(key, value): class SyncLang: - def __init__(self, extended, out_file='status.md'): + def __init__(self, extended): """ :param extended: boolean: if the keys with problems should be printed @@ -204,7 +209,6 @@ def __init__(self, extended, out_file='status.md'): self.extended = extended self.main_jabref_preferences = os.path.join(RES_DIR, "JabRef_en.properties") self.main_menu_preferences = os.path.join(RES_DIR, "Menu_en.properties") - self.markdown_output = out_file def status(self): """ @@ -350,7 +354,7 @@ def __update_properties(self, main_property_file, other_property_files): num_keys_obsolete = len(keys_obsolete) for missing_key in keys_missing: - # Missing keys are added with english translation by default. + # Missing keys are added with main translation by default. keys[missing_key] = main_keys_dict[missing_key] for obsolete_key in keys_obsolete: @@ -434,7 +438,7 @@ def __missing_keys(first_list, second_list): """ return list(set(first_list).difference(second_list)) - def status_create_markdown(self): + def status_create_markdown(self, markdown_file='status.md'): """ Creates a markdown file of the current status. """ @@ -465,7 +469,7 @@ def _percentage(whole, part): return 0 return int(part / whole * 100.0) - with codecs.open(self.markdown_output, "w", encoding="UTF-8") as status_file: + with codecs.open(markdown_file, "w", encoding="UTF-8") as status_file: status_file.write('### Localization files status (' + datetime.datetime.now().strftime( "%Y-%m-%d %H:%M") + ' - Branch `' + Git().get_current_branch() + '` `' + Git().get_current_hash_short() + '`)\n\n') status_file.write('Note: To get the current status from your local repository, run `python ./scripts/syncLang.py markdown`\n') @@ -473,39 +477,43 @@ def _percentage(whole, part): _write_properties(status_file, self.__all_menu_properties()) _write_properties(status_file, self.__all_jabref_properties()) - logging.info('Current status written to ' + self.markdown_output) + logging.info('Current status written to ' + markdown_file) def main(): - if len(sys.argv) == 2 and sys.argv[1] == "markdown": - SyncLang(extended=False, out_file='status.md').status_create_markdown() - - elif (len(sys.argv) == 2 or len(sys.argv) == 3) and sys.argv[1] == "update": - SyncLang(extended=len(sys.argv) == 3 and (sys.argv[2] == "-e" or sys.argv[2] == "--extended")).update_properties() - - elif (len(sys.argv) == 2 or len(sys.argv) == 3) and sys.argv[1] == "status": - SyncLang(extended=len(sys.argv) == 3 and (sys.argv[2] == "-e" or sys.argv[2] == "--extended")).status() - - else: - logging.info("""This program must be run from the JabRef base directory. - Usage: syncLang.py {markdown, status [-e | --extended], update [-e | --extended]} - Option can be one of the following: + def markdown(args): + SyncLang(extended=False).status_create_markdown() + def status(args): + SyncLang(extended=args.extended).status() + def update(args): + SyncLang(extended=args.extended).update_properties() + + + parser = argparse.ArgumentParser(add_help=True) + parser.description = "This script is used to synchronize the keys of different *.properties files." + + + shared_arguments = argparse.ArgumentParser(add_help=False) + extended_argument = shared_arguments.add_argument("-e", "--extended", help="Prints extended information about the process to the terminal", required=False, action='store_true', default=False) + + subcommands = parser.add_subparsers(title="Subcommands", description="Provide different options for the user") + + # markdown parser + markdown_parser = subcommands.add_parser("markdown", description="Creates a markdown file of the current status") + markdown_parser.set_defaults(func=markdown) + # TODO add argument to pass a file name for the markdown file - status [-e | --extended]: - prints the current status to the terminal - [-e | --extended]: - if the translations keys which create problems should be printed + # status parser + status_parser = subcommands.add_parser("status", description="Prints the current status to the terminal", parents=[shared_arguments]) + status_parser.set_defaults(func=status) - markdown: - Creates a markdown file of the current status and opens it + # update parser + update_parser = subcommands.add_parser("update", description="Compares all the localization files against the English one and fixes unambiguous duplicates, removes obsolete keys, adds missing keys, and sorts them", parents=[shared_arguments]) + update_parser.set_defaults(func=update) - update [-e | --extended]: - compares all the localization files against the English one and fixes unambiguous duplicates, - removes obsolete keys, adds missing keys, and sorts them - [-e | --extended]: - if the translations keys which create problems should be printed - """) + parsed_args = parser.parse_args() + parsed_args.func(parsed_args) if '__main__' == __name__: From c5eecef7ed48f5e04ab5d41b3a62f966656b14ef Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 21 Feb 2018 03:18:27 +0100 Subject: [PATCH 012/560] Reworked logging functionality, added new command-line options --- scripts/syncLang.py | 189 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 148 insertions(+), 41 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 65e75e6a7d5..733c6a340ba 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -1,7 +1,7 @@ # coding=utf-8 from __future__ import division -import argparse # Requires Python 2.7, should be safe to use with jython. +import argparse # Requires Python 2.7, should be safe to use with jython. import codecs import datetime import logging @@ -17,6 +17,7 @@ try: # Just to make sure not to break anything, in case py3.x is not supported. import pathlib + class PathFinder: """ This class is designed to automatically locate this script's path within the repository. @@ -201,14 +202,52 @@ def format_key_and_value(key, value): class SyncLang: - def __init__(self, extended): + def __init__(self, extended_logging=False): """ :param extended: boolean: if the keys with problems should be printed """ - self.extended = extended + self.extended_logging = extended_logging self.main_jabref_preferences = os.path.join(RES_DIR, "JabRef_en.properties") self.main_menu_preferences = os.path.join(RES_DIR, "Menu_en.properties") + + def set_extended_logging_enabled(self, value): + self.extended_logging = bool(value) + + def print_missing_keys_for_file(self, file_name): + file_status = self.__get_status_for_file(file_name) + if file_status: + keys_missing, _, _, _ = file_status + + if len(keys_missing) > 0: + logging.info("Printing missing keys for file: " + file_name) + self.__print_keys(keys_missing) + else: + logging.info("No missing keys found for file:" + file_name) + + def print_obsolete_keys_for_file(self, file_name): + file_status = self.__get_status_for_file(file_name) + if file_status: + _, keys_obsolete, _, _ = file_status + + if len(keys_obsolete) > 0: + + logging.info("Printing obsolete keys for file: " + file_name) + self.__print_keys(keys_obsolete) + else: + logging.info("No obsolete keys found for file: " + file_name) + + def print_duplicate_keys_for_file(self, file_name): + file_status = self.__get_status_for_file(file_name) + if file_status: + _, _, keys_duplicate, _ = file_status + + if len(keys_duplicate) > 0: + + logging.info("Printing duplicate keys for file: " + file_name) + self.__print_keys(keys_duplicate) + else: + logging.info("No duplicate keys found for file: " + file_name) def status(self): """ @@ -219,10 +258,10 @@ def status(self): self.__print_status_menu_properties() def __print_status_menu_properties(self): - self.__check_properties(main_property_file=self.main_menu_preferences, property_files=self.__all_menu_properties()) + self.__compare_properties(main_property_file=self.main_menu_preferences, property_files=self.__all_menu_properties()) def __print_status_jabref_properties(self): - self.__check_properties(main_property_file=self.main_jabref_preferences, property_files=self.__all_jabref_properties()) + self.__compare_properties(main_property_file=self.main_jabref_preferences, property_files=self.__all_jabref_properties()) def update_properties(self): """ @@ -238,8 +277,37 @@ def __update_menu_properties(self): def __update_jabref_properties(self): self.__update_properties(main_property_file=self.main_jabref_preferences, other_property_files=self.__other_jabref_properties()) + + def __get_main_file(self, file_name): + + file = os.path.join(RES_DIR, file_name) + + if file in self.__all_jabref_properties(): + return self.main_jabref_preferences + elif file in self.__all_menu_properties(): + return self.main_menu_preferences + + def __get_status_for_file(self, file_name): + main_file = self.__get_main_file(file_name) + if main_file: + main_lines = self.__read_lines_from_file(filename=main_file) + main_keys = Keys(main_lines) + + file = os.path.join(RES_DIR, file_name) + lines = self.__read_lines_from_file(file) + keys1 = Keys(lines) + keys = keys1.keys_from_lines() - def __check_properties(self, main_property_file, property_files): + keys_missing = self.__missing_keys(main_keys.keys_from_lines(), keys) + keys_obsolete = self.__missing_keys(keys, main_keys.keys_from_lines()) + keys_duplicates = keys1.find_duplicates() + keys_not_translated = keys1.empty_keys() + + return (keys_missing, keys_obsolete, keys_duplicates, keys_not_translated) + else: + logging.debug("Unable to find main file for: " + file_name) + + def __compare_properties(self, main_property_file, property_files): main_lines = self.__read_lines_from_file(filename=main_property_file) main_keys = Keys(main_lines) @@ -247,19 +315,19 @@ def __check_properties(self, main_property_file, property_files): for file in property_files: filename = self.__format_filename(filepath=file) lines = self.__read_lines_from_file(file) - keys1 = Keys(main_lines) + keys1 = Keys(lines) keys = keys1.keys_from_lines() keys_missing = self.__missing_keys(main_keys.keys_from_lines(), keys) keys_obsolete = self.__missing_keys(keys, main_keys.keys_from_lines()) - keys_duplicate = Keys(lines).find_duplicates() - keys_not_translated = Keys(lines=lines).empty_keys() + keys_duplicates = keys1.find_duplicates() + keys_not_translated = keys1.empty_keys() num_keys = len(keys) num_keys_missing = len(keys_missing) num_keys_not_translated = len(keys_not_translated) num_keys_obsolete = len(keys_obsolete) - num_keys_duplicate = len(keys_duplicate) + num_keys_duplicate = len(keys_duplicates) num_keys_translated = num_keys - num_keys_not_translated log = logging.error if num_keys_missing != 0 or num_keys_not_translated != 0 or num_keys_obsolete != 0 or num_keys_duplicate != 0 else logging.info @@ -268,23 +336,23 @@ def __check_properties(self, main_property_file, property_files): log = logging.error if num_keys_not_translated != 0 else logging.info log("\t{} not translated keys".format(num_keys_not_translated)) - if self.extended and num_keys_not_translated != 0: - logging.info("\t\t{}".format(", ".join(keys_not_translated))) + if self.extended_logging and num_keys_not_translated != 0: + self.__print_keys(keys_not_translated) log = logging.error if num_keys_missing != 0 else logging.info log("\t{} missing keys".format(num_keys_missing)) - if self.extended and num_keys_missing != 0: - logging.info("\t\t{}".format(", ".join(keys_missing))) + if self.extended_logging and num_keys_missing != 0: + self.__print_keys(keys_missing) log = logging.error if num_keys_obsolete != 0 else logging.info log("\t{} obsolete keys".format(num_keys_obsolete)) - if self.extended and num_keys_obsolete != 0: - logging.info("\t\t{}".format(", ".join(keys_obsolete))) + if self.extended_logging and num_keys_obsolete != 0: + self.__print_keys(keysobsolete) log = logging.error if num_keys_duplicate != 0 else logging.info log("\t{} duplicates".format(num_keys_duplicate)) - if self.extended and num_keys_duplicate != 0: - logging.info("\t\t{}".format(", ".join(keys_duplicate))) + if self.extended_logging and num_keys_duplicate != 0: + self.__print_keys(keys_duplicates) def __all_menu_properties(self): """ @@ -303,7 +371,7 @@ def __other_menu_properties(self): def __all_jabref_properties(self): """ - :return: list of strings: all the JabRef_*.preferences files with the english at the beginning + :return: list of strings: all the JabRef_*.preferences file paths with the english at the beginning """ jabref_property_files = sorted(self.__other_jabref_properties()) jabref_property_files.insert(0, self.main_jabref_preferences) @@ -311,7 +379,7 @@ def __all_jabref_properties(self): def __other_jabref_properties(self): """ - :return: list of strings: all the JabRef_*.preferences files without the english one + :return: list of strings: all the JabRef_*.preferences file paths without the english one """ jabref_property_files = [s for s in os.listdir(RES_DIR) if (s.startswith('JabRef_') and not (s.startswith('JabRef_en')))] return [os.path.join(RES_DIR, file) for file in jabref_property_files] @@ -327,8 +395,8 @@ def __update_properties(self, main_property_file, other_property_files): logging.error("There are {num_duplicates} duplicates in {file}, please fix them manually".format(num_duplicates=num_main_duplicates, file=self.__format_filename( filepath=main_property_file))) - if self.extended: - logging.info("\t{}".format(", ".join(main_duplicates))) + if self.extended_logging: + self.__print_keys(main_duplicates) return for other_property_file in other_property_files: @@ -343,8 +411,8 @@ def __update_properties(self, main_property_file, other_property_files): if num_not_fixed != 0: logging.error("There are {num_not_fixed_duplicates} ambiguous duplicates in {file}, please fix them manually".format( num_not_fixed_duplicates=num_not_fixed, file=filename)) - if self.extended: - logging.error("\t{}".format(", ".join(not_fixed))) + if self.extended_logging: + self.__print_keys(not_fixed) continue keys_missing = self.__missing_keys(main_keys.keys_from_lines(), keys) @@ -381,18 +449,18 @@ def __update_properties(self, main_property_file, other_property_files): logging.info("Processing file '{file}' with {num_keys} Keys".format(file=filename, num_keys=num_keys)) if num_fixed != 0: logging.info("\tfixed {} unambiguous duplicates".format(num_fixed)) - if self.extended: - logging.info("\t\t{}".format(", ".join(fixed))) + if self.extended_logging: + self.__print_keys(fixed) if num_keys_missing != 0: logging.info("\tadded {} missing keys".format(num_keys_missing)) - if self.extended: - logging.info("\t\t{}".format(", ".join(keys_missing))) + if self.extended_logging: + self.__print_keys(keys_missing) if num_keys_obsolete != 0: logging.info("\tdeleted {} obsolete keys".format(num_keys_obsolete)) - if self.extended: - logging.info("\t\t{}".format(", ".join(keys_obsolete))) + if self.extended_logging: + self.__print_keys(keys_obsolete) if sorted_lines: logging.info("\thas been sorted successfully") @@ -403,7 +471,7 @@ def __format_filename(filepath): removes the res_dir path :param filepath: string - :return: string + :return: pure file name of this file path (including file extension e.g. *.txt) """ return filepath.replace("{}\\".format(RES_DIR), "") @@ -437,6 +505,11 @@ def __missing_keys(first_list, second_list): :return: list of unicode strings """ return list(set(first_list).difference(second_list)) + + @staticmethod + def __print_keys(keys, logger=logging.info): + for key in keys: + logger("\t{}\n".format(key)) def status_create_markdown(self, markdown_file='status.md'): """ @@ -482,18 +555,35 @@ def _percentage(whole, part): def main(): - def markdown(args): - SyncLang(extended=False).status_create_markdown() - def status(args): - SyncLang(extended=args.extended).status() - def update(args): - SyncLang(extended=args.extended).update_properties() + syncer = SyncLang() + def markdown_command(args): + syncer.set_extended_logging_enabled(False) + syncer.status_create_markdown() + + def status_command(args): + syncer.set_extended_logging_enabled(args.extended) + syncer.status() + + def update_command(args): + syncer.set_extended_logging_enabled(args.extended) + syncer.update_properties() + + def print_missing(args): + file_name = args.f + syncer.print_missing_keys_for_file(file_name) + + def print_obsolete(args): + file_name = args.f + syncer.print_obsolete_keys_for_file(file_name) + + def print_duplicates(args): + file_name = args.f + syncer.print_duplicate_keys_for_file(file_name) parser = argparse.ArgumentParser(add_help=True) parser.description = "This script is used to synchronize the keys of different *.properties files." - shared_arguments = argparse.ArgumentParser(add_help=False) extended_argument = shared_arguments.add_argument("-e", "--extended", help="Prints extended information about the process to the terminal", required=False, action='store_true', default=False) @@ -501,16 +591,33 @@ def update(args): # markdown parser markdown_parser = subcommands.add_parser("markdown", description="Creates a markdown file of the current status") - markdown_parser.set_defaults(func=markdown) + markdown_parser.set_defaults(func=markdown_command) # TODO add argument to pass a file name for the markdown file # status parser status_parser = subcommands.add_parser("status", description="Prints the current status to the terminal", parents=[shared_arguments]) - status_parser.set_defaults(func=status) + status_parser.set_defaults(func=status_command) # update parser update_parser = subcommands.add_parser("update", description="Compares all the localization files against the English one and fixes unambiguous duplicates, removes obsolete keys, adds missing keys, and sorts them", parents=[shared_arguments]) - update_parser.set_defaults(func=update) + update_parser.set_defaults(func=update_command) + + # print parser + print_parser = subcommands.add_parser("print", description="Prints specific status info to the console") + + shared_print_arguments = argparse.ArgumentParser(add_help=False) + file_argument = shared_print_arguments.add_argument("-f", "-file", help="Specifies a file for the command to run with", required=True, action='store') + + print_options = print_parser.add_subparsers(title="Print Options", description="Different options for printing") + + missing_parser = print_options.add_parser("missing", description="Prints all missing keys", parents=[shared_print_arguments]) + missing_parser.set_defaults(func=print_missing) + + obsolete_parser = print_options.add_parser("obsolete", description="Prints all obsolete keys", parents=[shared_print_arguments]) + obsolete_parser.set_defaults(func=print_obsolete) + + duplicates_parser = print_options.add_parser("duplicates", description="Prints all duplicate keys", parents=[shared_print_arguments]) + duplicates_parser.set_defaults(func=print_duplicates) parsed_args = parser.parse_args() parsed_args.func(parsed_args) From 35b7f8330fb2694afbfc7f5e2be056276d415b3a Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 21 Feb 2018 03:33:51 +0100 Subject: [PATCH 013/560] =?UTF-8?q?small=20fix=20on=20=C2=B4getJabRefBaseD?= =?UTF-8?q?irectory=C2=B4=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/syncLang.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 733c6a340ba..09d6403ca39 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -39,6 +39,9 @@ def getJabRefBaseDirectory(): Searches the script's path backwards until it finds the matching base directory. :return the path to JabRef's base directory as pathlib.Path object. """ + if pathlib.Path.cwd().name == 'jabref': + return pathlib.Path.cwd + scriptLocation = PathFinder.getScriptLocation() for parent in scriptLocation.parents: if parent.name == 'jabref': From ded149b0b1da11a6c92ae9e9212206da8436b82b Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 21 Feb 2018 03:34:32 +0100 Subject: [PATCH 014/560] forgot brackets... --- scripts/syncLang.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 09d6403ca39..338a504ed74 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -40,7 +40,7 @@ def getJabRefBaseDirectory(): :return the path to JabRef's base directory as pathlib.Path object. """ if pathlib.Path.cwd().name == 'jabref': - return pathlib.Path.cwd + return pathlib.Path.cwd() scriptLocation = PathFinder.getScriptLocation() for parent in scriptLocation.parents: From 1379dad815880963bfaf1149498cda43ecd9412e Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 21 Feb 2018 03:47:58 +0100 Subject: [PATCH 015/560] =?UTF-8?q?Removed=20=C2=B4getScriptLocation=C2=B4?= =?UTF-8?q?=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/syncLang.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 338a504ed74..0a679231f1a 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -26,26 +26,20 @@ class PathFinder: requires Python 3.4 or higher """ - @staticmethod - def getScriptLocation(): - """ - :return the path this script is currently located as pathlib.Path object. - """ - return pathlib.Path(__file__) - @staticmethod def getJabRefBaseDirectory(): """ Searches the script's path backwards until it finds the matching base directory. :return the path to JabRef's base directory as pathlib.Path object. """ - if pathlib.Path.cwd().name == 'jabref': - return pathlib.Path.cwd() + cwd = pathlib.Path.cwd() + if cwd.name == 'jabref': + return cwd - scriptLocation = PathFinder.getScriptLocation() - for parent in scriptLocation.parents: + for parent in cwd.parents: if parent.name == 'jabref': return parent + # TODO What to do if base directory could not be found? # Important directories of the JabRef repository JABREF_BASE_DIRECTORY = PathFinder.getJabRefBaseDirectory() From e7be7ef3eaa55691eaee32b5db804719880295e1 Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 21 Feb 2018 03:50:45 +0100 Subject: [PATCH 016/560] =?UTF-8?q?Added=20=C2=B4BASE=5FDIRECTORY=5FNAME?= =?UTF-8?q?=C2=B4=20variable=20to=20PathFinder=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/syncLang.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 0a679231f1a..3046eb42b75 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -26,6 +26,8 @@ class PathFinder: requires Python 3.4 or higher """ + BASE_DIRECTORY_NAME = 'jabref' + @staticmethod def getJabRefBaseDirectory(): """ @@ -33,11 +35,11 @@ def getJabRefBaseDirectory(): :return the path to JabRef's base directory as pathlib.Path object. """ cwd = pathlib.Path.cwd() - if cwd.name == 'jabref': + if cwd.name == PathFinder.BASE_DIRECTORY_NAME: return cwd for parent in cwd.parents: - if parent.name == 'jabref': + if parent.name == PathFinder.BASE_DIRECTORY_NAME: return parent # TODO What to do if base directory could not be found? From cb21464b6173d9a52d5b2abeb6c20825d5099746 Mon Sep 17 00:00:00 2001 From: Stefan Date: Thu, 22 Feb 2018 12:53:58 +0100 Subject: [PATCH 017/560] Print commands will now use localization directory by default --- scripts/syncLang.py | 52 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 3046eb42b75..8dfb267edab 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -213,6 +213,14 @@ def __init__(self, extended_logging=False): def set_extended_logging_enabled(self, value): self.extended_logging = bool(value) + def print_missing_keys(self): + for file in self.__other_jabref_properties(): + file_name = self.__format_filename(file) + self.print_missing_keys_for_file(file_name) + for file in self.__other_menu_properties(): + file_name = self.__format_filename(file) + self.print_missing_keys_for_file(file_name) + def print_missing_keys_for_file(self, file_name): file_status = self.__get_status_for_file(file_name) if file_status: @@ -223,6 +231,15 @@ def print_missing_keys_for_file(self, file_name): self.__print_keys(keys_missing) else: logging.info("No missing keys found for file:" + file_name) + + def print_obsolete_keys(self): + for file in self.__other_jabref_properties(): + file_name = self.__format_filename(file) + self.print_obsolete_keys_for_file(file_name) + for file in self.__other_menu_properties(): + file_name = self.__format_filename(file) + self.print_obsolete_keys_for_file(file_name) + def print_obsolete_keys_for_file(self, file_name): file_status = self.__get_status_for_file(file_name) @@ -235,6 +252,14 @@ def print_obsolete_keys_for_file(self, file_name): self.__print_keys(keys_obsolete) else: logging.info("No obsolete keys found for file: " + file_name) + + def print_duplicate_keys(self): + for file in self.__other_jabref_properties(): + file_name = self.__format_filename(file) + self.print_duplicate_keys_for_file(file_name) + for file in self.__other_menu_properties(): + file_name = self.__format_filename(file) + self.print_duplicate_keys_for_file(file_name) def print_duplicate_keys_for_file(self, file_name): file_status = self.__get_status_for_file(file_name) @@ -569,16 +594,25 @@ def update_command(args): syncer.update_properties() def print_missing(args): - file_name = args.f - syncer.print_missing_keys_for_file(file_name) + file_name = args.file + if file_name: + syncer.print_missing_keys_for_file(file_name) + else: + syncer.print_missing_keys() def print_obsolete(args): - file_name = args.f - syncer.print_obsolete_keys_for_file(file_name) + file_name = args.file + if file_name: + syncer.print_obsolete_keys_for_file(file_name) + else: + syncer.print_obsolete_keys() def print_duplicates(args): - file_name = args.f - syncer.print_duplicate_keys_for_file(file_name) + file_name = args.file + if file_name: + syncer.print_duplicate_keys_for_file(file_name) + else: + syncer.print_duplicate_keys() parser = argparse.ArgumentParser(add_help=True) parser.description = "This script is used to synchronize the keys of different *.properties files." @@ -586,7 +620,7 @@ def print_duplicates(args): shared_arguments = argparse.ArgumentParser(add_help=False) extended_argument = shared_arguments.add_argument("-e", "--extended", help="Prints extended information about the process to the terminal", required=False, action='store_true', default=False) - subcommands = parser.add_subparsers(title="Subcommands", description="Provide different options for the user") + subcommands = parser.add_subparsers(title="Subcommands", description="Provide different options for the user", dest="subcommand") # markdown parser markdown_parser = subcommands.add_parser("markdown", description="Creates a markdown file of the current status") @@ -605,9 +639,9 @@ def print_duplicates(args): print_parser = subcommands.add_parser("print", description="Prints specific status info to the console") shared_print_arguments = argparse.ArgumentParser(add_help=False) - file_argument = shared_print_arguments.add_argument("-f", "-file", help="Specifies a file for the command to run with", required=True, action='store') + file_argument = shared_print_arguments.add_argument("-f", "--file", help="Specifies a file for the command to run with", required=False, action='store') - print_options = print_parser.add_subparsers(title="Print Options", description="Different options for printing") + print_options = print_parser.add_subparsers(title="Print Options", description="Different options for printing", dest="print_option_name") missing_parser = print_options.add_parser("missing", description="Prints all missing keys", parents=[shared_print_arguments]) missing_parser.set_defaults(func=print_missing) From fb3bba0b751cf29253f548d51319b2ddd3de07db Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 28 Feb 2018 20:45:33 +0100 Subject: [PATCH 018/560] Missing keys will no longer be written to the properties files --- scripts/syncLang.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 8dfb267edab..0dbe32d86b6 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -445,9 +445,9 @@ def __update_properties(self, main_property_file, other_property_files): num_keys_missing = len(keys_missing) num_keys_obsolete = len(keys_obsolete) - for missing_key in keys_missing: + # for missing_key in keys_missing: # Missing keys are added with main translation by default. - keys[missing_key] = main_keys_dict[missing_key] + # keys[missing_key] = main_keys_dict[missing_key] for obsolete_key in keys_obsolete: del keys[obsolete_key] From 432a92bb31d9737e6466cf7d64e89020ecbefdf6 Mon Sep 17 00:00:00 2001 From: Stefan Date: Fri, 2 Mar 2018 15:44:05 +0100 Subject: [PATCH 019/560] Fixed missing key issue --- scripts/syncLang.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/syncLang.py b/scripts/syncLang.py index 0dbe32d86b6..0b2386b3617 100644 --- a/scripts/syncLang.py +++ b/scripts/syncLang.py @@ -456,9 +456,10 @@ def __update_properties(self, main_property_file, other_property_files): for line in main_lines: key = main_keys.key_from_line(line) if key is not None: - # Do not write empty keys - if keys[key] != "": - other_lines_to_write.append(Keys.format_key_and_value(key=key, value=keys[key]) + "\n") + if keys.has_key(key): + # Do not write empty keys + if keys[key] != "": + other_lines_to_write.append(Keys.format_key_and_value(key=key, value=keys[key]) + "\n") else: other_lines_to_write.append(line) From 1c690aadafeffc8b5635674d0198c1ab3c65d228 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 25 Aug 2019 17:45:23 +0200 Subject: [PATCH 020/560] Remove testing on OracleJDK - we stick to OpenJDK only --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 05c4029729a..194315b47ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: java jdk: - - oraclejdk11 - openjdk11 # we test at Ubuntu Trusty (Ubuntu 14.04 LTS) From cc53fb8eea6b0dd16ac08daf4f6a6571e9e0adca Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 25 Aug 2019 22:32:32 +0200 Subject: [PATCH 021/560] Show development information --- CHANGELOG.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ build.gradle | 4 ++-- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e553bea82ad..7c9853c1f24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,64 @@ Here, the categories "Changed" for added and changed functionality, "Removed" for removed functionality are used. We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#NUM`. + +## [Unreleased] + +### Changed + +### Fixed + +### Removed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## [5.0-alpha] – 2019-08-25 diff --git a/build.gradle b/build.gradle index a18de9d2004..635cac253d4 100644 --- a/build.gradle +++ b/build.gradle @@ -50,8 +50,8 @@ apply plugin: LocalizationPlugin apply from: 'eclipse.gradle' group = "org.jabref" -version = "5.0-alpha" -project.ext.threeDotVersion = "5.0.0.0" +version = "5.0-dev" +project.ext.threeDotVersion = "5.0.0.1" project.ext.install4jDir = hasProperty("install4jDir") ? getProperty("install4jDir") : (OperatingSystem.current().isWindows() ? 'C:/Program Files/install4j8' : 'install4j8') sourceCompatibility = 11 targetCompatibility = 11 From c7a0ae774342a8659c6dcf6dabfe282e5bdefc0f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 07:42:40 +0000 Subject: [PATCH 022/560] Bump archunit-junit5-api from 0.10.2 to 0.11.0 Bumps [archunit-junit5-api](https://github.com/TNG/ArchUnit) from 0.10.2 to 0.11.0. - [Release notes](https://github.com/TNG/ArchUnit/releases) - [Commits](https://github.com/TNG/ArchUnit/compare/0.10.2...v0.11.0) Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 635cac253d4..6e375217ccb 100644 --- a/build.gradle +++ b/build.gradle @@ -201,7 +201,7 @@ dependencies { testCompile 'org.reflections:reflections:0.9.11' testCompile 'org.xmlunit:xmlunit-core:2.6.3' testCompile 'org.xmlunit:xmlunit-matchers:2.6.3' - testCompile 'com.tngtech.archunit:archunit-junit5-api:0.10.2' + testCompile 'com.tngtech.archunit:archunit-junit5-api:0.11.0' //testRuntime 'com.tngtech.archunit:archunit-junit5-engine:0.11.0' testCompile 'com.tngtech.archunit:archunit-junit5-api:0.11.0' testCompile "org.testfx:testfx-core:4.0.15-alpha" From 9129502072b9eb388f8a72986b2c85c4bbcc08de Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 07:46:19 +0000 Subject: [PATCH 023/560] Bump byte-buddy-parent from 1.9.13 to 1.10.1 Bumps [byte-buddy-parent](https://github.com/raphw/byte-buddy) from 1.9.13 to 1.10.1. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.9.13...byte-buddy-1.10.1) Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 635cac253d4..71e78bff2de 100644 --- a/build.gradle +++ b/build.gradle @@ -193,7 +193,7 @@ dependencies { testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.5.1' testCompile 'org.junit.platform:junit-platform-launcher:1.5.1' - testCompile 'net.bytebuddy:byte-buddy-parent:1.9.13' + testCompile 'net.bytebuddy:byte-buddy-parent:1.10.1' testRuntime 'org.apache.logging.log4j:log4j-core:2.12.0' testRuntime 'org.apache.logging.log4j:log4j-jul:2.12.1' testCompile 'org.mockito:mockito-core:3.0.0' From b43e4933617f330f782034e23dbfdf9aeb91a8fb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 07:47:34 +0000 Subject: [PATCH 024/560] Bump org.beryx.jlink from 2.14.1 to 2.15.0 Bumps org.beryx.jlink from 2.14.1 to 2.15.0. Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 635cac253d4..e8a4bc38ee2 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ plugins { id 'com.github.ben-manes.versions' version '0.22.0' id 'org.javamodularity.moduleplugin' version '1.5.0' id 'org.openjfx.javafxplugin' version '0.0.8' - id 'org.beryx.jlink' version '2.14.1' + id 'org.beryx.jlink' version '2.15.0' } // use the gradle build scan feature: https://scans.gradle.com/get-started From 38431cf99c89e883615ca0a5275a12a74fdd4eee Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Mon, 26 Aug 2019 11:19:10 +0200 Subject: [PATCH 025/560] Automatically merge minor dependency updates from dependabot --- .dependabot/config.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.dependabot/config.yml b/.dependabot/config.yml index ef91c3889ff..e7ec7f70538 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -5,7 +5,15 @@ update_configs: - package_manager: "java:gradle" directory: "/" update_schedule: "weekly" - + automerged_updates: + - match: + dependency_type: "all" + update_type: "semver:minor" + - package_manager: "submodules" directory: "/" update_schedule: "weekly" + automerged_updates: + - match: + dependency_type: "all" + update_type: "all" From 84436d7954d7a63a42794b4057c5bfb2f8e819eb Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 27 Aug 2019 21:14:46 +0200 Subject: [PATCH 026/560] Switch to Jakarta libraries (#5247) * Move to jakarta https://wiki.eclipse.org/New_Maven_Coordinates * Revert "Switch from tika-parsers to tika-core (#5217)" This reverts commit 29cf4f20 * Revert "Revert "Switch from tika-parsers to tika-core (#5217)"" This reverts commit be558fe0 * Fix build * Fix build --- build.gradle | 27 ++-- src/main/java/module-info.java | 117 +++++++++--------- .../org/jabref/logic/l10n/Localization.java | 2 +- 3 files changed, 76 insertions(+), 70 deletions(-) diff --git a/build.gradle b/build.gradle index 5d74f386dec..34a396dce45 100644 --- a/build.gradle +++ b/build.gradle @@ -102,9 +102,10 @@ configurations { errorprone libreoffice - // TODO: Workaround for "ResolutionException: Modules java.annotation and jsr305 export package javax.annotation to module httpcore.nio" + // TODO: Remove the following workaround for split error messages such as + // error: module java.xml.bind reads package javax.annotation from both jsr305 and java.annotation compile { - exclude group: 'com.google.code.findbugs', module: 'jsr305' + exclude group: "javax.activation" } } @@ -149,9 +150,12 @@ dependencies { compile 'org.postgresql:postgresql:42.2.6' - compile 'com.google.guava:guava:28.0-jre' + compile ('com.google.guava:guava:28.0-jre') { + // TODO: Remove this as soon as https://github.com/google/guava/issues/2960 is fixed + exclude module: "jsr305" + } - compile group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2' + compile group: 'jakarta.annotation', name: 'jakarta.annotation-api', version: '1.3.4' // JavaFX stuff compile 'de.jensd:fontawesomefx-commons:11.0' @@ -161,7 +165,7 @@ dependencies { compile 'org.fxmisc.easybind:easybind:1.0.3' compile 'org.fxmisc.flowless:flowless:0.6.1' compile 'org.fxmisc.richtext:richtextfx:0.10.1' - compile 'javax.inject:javax.inject:1' + compile group: 'org.glassfish.hk2.external', name: 'jakarta.inject', version: '2.6.1' compile 'com.jfoenix:jfoenix:9.0.9' compile 'org.controlsfx:controlsfx:11.0.0' @@ -177,9 +181,9 @@ dependencies { compile 'de.undercouch:citeproc-java:1.0.1' - compile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1' - compile group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.1' - compile 'com.sun.activation:javax.activation:1.2.0' + compile group: 'jakarta.activation', name: 'jakarta.activation-api', version: '1.2.1' + compile group: 'jakarta.xml.bind', name: 'jakarta.xml.bind-api', version: '2.3.2' + compile group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.2' compile 'com.github.tomtung:latex2unicode_2.12:0.2.6' @@ -198,7 +202,9 @@ dependencies { testRuntime 'org.apache.logging.log4j:log4j-jul:2.12.1' testCompile 'org.mockito:mockito-core:3.0.0' //testCompile 'com.github.tomakehurst:wiremock:2.24.1' - testCompile 'org.reflections:reflections:0.9.11' + testCompile ('org.reflections:reflections:0.9.11') { + exclude module: "jsr305" + } testCompile 'org.xmlunit:xmlunit-core:2.6.3' testCompile 'org.xmlunit:xmlunit-matchers:2.6.3' testCompile 'com.tngtech.archunit:archunit-junit5-api:0.11.0' @@ -604,7 +610,7 @@ jlink { // The pom.xml associated with such a non-modular artifact does not mention that the artifact depends on the removed code // (because the artifact was published when this code was still available in the JDK). forceMerge "javafx" - forceMerge "controlsfx", "bcprov", "jaxb", "javax", "istack", "stax", "log4j" + forceMerge "controlsfx", "bcprov", "jaxb", "istack", "stax", "log4j" // TODO: Remove the following correction to the merged module // The module descriptor automatically generated by the plugin for the merged module contained some invalid entries. @@ -637,7 +643,6 @@ jlink { provides 'org.controlsfx.glyphfont.GlyphFont' with 'org.controlsfx.glyphfont.FontAwesome' provides 'org.apache.commons.logging.LogFactory' with 'org.apache.logging.log4j.jcl.LogFactoryImpl' provides 'org.slf4j.spi.SLF4JServiceProvider' with 'org.apache.logging.slf4j.SLF4JServiceProvider' - provides 'javax.annotation.processing.Processor' with 'org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor' provides 'com.microsoft.applicationinsights.core.dependencies.io.grpc.ServerProvider' with 'com.microsoft.applicationinsights.core.dependencies.io.grpc.netty.shaded.io.grpc.netty.NettyServerProvider' provides 'com.microsoft.applicationinsights.core.dependencies.io.grpc.NameResolverProvider' with 'com.microsoft.applicationinsights.core.dependencies.io.grpc.internal.DnsNameResolverProvider' provides 'java.security.Provider' with 'org.bouncycastle.jce.provider.BouncyCastleProvider', 'org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider' diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index b0ba567ae84..315e4bec469 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,74 +1,75 @@ open module org.jabref { - exports org.jabref; + exports org.jabref; - exports org.jabref.gui; - exports org.jabref.gui.logging; - exports org.jabref.gui.maintable; - exports org.jabref.gui.specialfields; + exports org.jabref.gui; + exports org.jabref.gui.logging; + exports org.jabref.gui.maintable; + exports org.jabref.gui.specialfields; - exports org.jabref.model.database; + exports org.jabref.model.database; - exports org.jabref.logic; - exports org.jabref.logic.citationstyle; - exports org.jabref.logic.search; + exports org.jabref.logic; + exports org.jabref.logic.citationstyle; + exports org.jabref.logic.search; - // Swing - requires java.desktop; + // Swing + requires java.desktop; - // SQL - requires java.sql; + // SQL + requires java.sql; - // JavaFX - requires javafx.graphics; - requires javafx.swing; - requires javafx.controls; - requires javafx.web; - requires javafx.fxml; - requires afterburner.fx; - requires com.jfoenix; - requires de.saxsys.mvvmfx; - requires de.jensd.fx.fontawesomefx.commons; - requires de.jensd.fx.fontawesomefx.materialdesignicons; - requires org.controlsfx.controls; + // JavaFX + requires javafx.graphics; + requires javafx.swing; + requires javafx.controls; + requires javafx.web; + requires javafx.fxml; + requires afterburner.fx; + requires com.jfoenix; + requires de.saxsys.mvvmfx; + requires de.jensd.fx.fontawesomefx.commons; + requires de.jensd.fx.fontawesomefx.materialdesignicons; + requires org.controlsfx.controls; - provides com.airhacks.afterburner.views.ResourceLocator - with org.jabref.gui.util.JabRefResourceLocator; + provides com.airhacks.afterburner.views.ResourceLocator + with org.jabref.gui.util.JabRefResourceLocator; - provides com.airhacks.afterburner.injection.PresenterFactory - with org.jabref.gui.DefaultInjector; + provides com.airhacks.afterburner.injection.PresenterFactory + with org.jabref.gui.DefaultInjector; - // Logging - requires org.slf4j; - requires org.apache.logging.log4j; - requires org.apache.logging.log4j.core; - requires applicationinsights.logging.log4j2; + // Logging + requires org.slf4j; + requires org.apache.logging.log4j; + requires org.apache.logging.log4j.core; + requires applicationinsights.logging.log4j2; - // Preferences and XML - requires java.prefs; - requires java.xml.bind; - requires jdk.xml.dom; + // Preferences and XML + requires java.prefs; + requires java.xml.bind; + requires jdk.xml.dom; - // Annotations (@PostConstruct) - requires java.annotation; + // Annotations (@PostConstruct) + requires java.annotation; - // Microsoft application insights - requires applicationinsights.core; + // Microsoft application insights + requires applicationinsights.core; - // Libre Office - requires org.jabref.thirdparty.libreoffice; + // Libre Office + requires org.jabref.thirdparty.libreoffice; - // Other modules - requires commons.logging; - requires com.google.common; - requires easybind; - requires javax.inject; - requires pdfbox; - requires reactfx; - requires commons.cli; - requires httpclient; - requires com.github.tomtung.latex2unicode; - requires jbibtex; - requires citeproc.java; - requires antlr.runtime; - requires commons.lang3; + // Other modules + requires commons.logging; + requires com.google.common; + requires easybind; + requires jakarta.inject; + requires pdfbox; + requires reactfx; + requires commons.cli; + requires httpclient; + requires com.github.tomtung.latex2unicode; + requires jbibtex; + requires citeproc.java; + requires antlr.runtime; + requires commons.lang3; + requires xmpbox; } diff --git a/src/main/java/org/jabref/logic/l10n/Localization.java b/src/main/java/org/jabref/logic/l10n/Localization.java index c62a92db221..1ceecaad954 100644 --- a/src/main/java/org/jabref/logic/l10n/Localization.java +++ b/src/main/java/org/jabref/logic/l10n/Localization.java @@ -66,7 +66,7 @@ public static String lang(String key, String... params) { public static void setLanguage(Language language) { Optional knownLanguage = Language.convertToSupportedLocale(language); final Locale defaultLocale = Locale.getDefault(); - if (!knownLanguage.isPresent()) { + if (knownLanguage.isEmpty()) { LOGGER.warn("Language " + language + " is not supported by JabRef (Default:" + defaultLocale + ")"); setLanguage(Language.ENGLISH); return; From 0df8ab33f1e993cb0e625e7535bcf2afe868875a Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 27 Aug 2019 21:41:00 +0200 Subject: [PATCH 027/560] Compile with intellj --- .gitignore | 3 ++- .idea/runConfigurations/JabRef_Main.xml | 13 +++++++++++++ build.gradle | 6 ++++-- 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 .idea/runConfigurations/JabRef_Main.xml diff --git a/.gitignore b/.gitignore index 0dc966d62cd..18c9d5bfff6 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,8 @@ snap/.snapcraft/ ui/ # IntelliJ IDEA -.idea/ +.idea/* +!.idea/runConfigurations/ *.ipr *.iml diff --git a/.idea/runConfigurations/JabRef_Main.xml b/.idea/runConfigurations/JabRef_Main.xml new file mode 100644 index 00000000000..780974d6408 --- /dev/null +++ b/.idea/runConfigurations/JabRef_Main.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 34a396dce45..8aca0a7d039 100644 --- a/build.gradle +++ b/build.gradle @@ -185,9 +185,11 @@ dependencies { compile group: 'jakarta.xml.bind', name: 'jakarta.xml.bind-api', version: '2.3.2' compile group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.2' - compile 'com.github.tomtung:latex2unicode_2.12:0.2.6' + compile ('com.github.tomtung:latex2unicode_2.12:0.2.6') { + exclude module: 'fastparse_2.12' + } - errorprone 'com.google.errorprone:error_prone_core:2.3.2' + //errorprone 'com.google.errorprone:error_prone_core:2.3.2' compile group: 'com.microsoft.azure', name: 'applicationinsights-core', version: '2.4.1' compile group: 'com.microsoft.azure', name: 'applicationinsights-logging-log4j2', version: '2.4.1' From 9e2bed02d086692b2815ad3c5255cd3897c883b4 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 27 Aug 2019 22:07:40 +0200 Subject: [PATCH 028/560] disable error prone --- build.gradle | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 8aca0a7d039..04ea37d1ef7 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ plugins { id 'com.github.johnrengelman.shadow' version '5.1.0' id "com.simonharrer.modernizer" version '1.8.0-1' id 'me.champeau.gradle.jmh' version '0.4.8' - id 'net.ltgt.errorprone' version '0.8.1' + //id 'net.ltgt.errorprone' version '0.8.1' id 'com.github.ben-manes.versions' version '0.22.0' id 'org.javamodularity.moduleplugin' version '1.5.0' id 'org.openjfx.javafxplugin' version '0.0.8' @@ -99,7 +99,7 @@ repositories { } configurations { - errorprone + //errorprone libreoffice // TODO: Remove the following workaround for split error messages such as @@ -189,7 +189,16 @@ dependencies { exclude module: 'fastparse_2.12' } - //errorprone 'com.google.errorprone:error_prone_core:2.3.2' + /* + TODO: Reenable error prone as soon as https://github.com/google/error-prone/issues/1210 is fixed + errorprone ('com.google.errorprone:error_prone_core:2.3.2') { + exclude module: "jsr305" + exclude group: 'org.checkerframework', module: 'checker-qual' + exclude group: 'com.google.errorprone', module: 'error_prone_annotation' + exclude group: 'com.google.errorprone', module: 'error_prone_annotations' + exclude group: 'com.google.errorprone', module: 'error_prone_check_api' + } + */ compile group: 'com.microsoft.azure', name: 'applicationinsights-core', version: '2.4.1' compile group: 'com.microsoft.azure', name: 'applicationinsights-logging-log4j2', version: '2.4.1' From 78f7cf4595f125eb465f19626f325f3a25caa6fc Mon Sep 17 00:00:00 2001 From: Robin Lichtenthaeler Date: Thu, 29 Aug 2019 11:26:01 +0200 Subject: [PATCH 029/560] adjusted gitignore to properly match icon classes --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0dc966d62cd..736fbd75885 100644 --- a/.gitignore +++ b/.gitignore @@ -350,4 +350,4 @@ lib/ojdbc.jar !/buildSrc/src/main/groovy/org/jabref/build # do not ignore JabRef icons (they are ignored by the macos setting above) -!gui/icon/**/* +!src/main/java/org/jabref/gui/icon From ecdeaedbd2dfc8896cf557c243bd34c020e780bd Mon Sep 17 00:00:00 2001 From: Robin Lichtenthaeler Date: Thu, 29 Aug 2019 11:26:53 +0200 Subject: [PATCH 030/560] workaround for issue #5245, added custom Factory class --- .../gui/copyfiles/CopyFilesDialogView.java | 4 +- .../gui/fieldeditors/LinkedFilesEditor.java | 6 +-- .../java/org/jabref/gui/icon/IconTheme.java | 1 + .../icon/JabRefMaterialDesignIconFactory.java | 51 +++++++++++++++++++ .../gui/util/ViewModelListCellFactory.java | 6 +-- 5 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIconFactory.java diff --git a/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java b/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java index 52f46bf22d3..a0b5cc53d57 100644 --- a/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java +++ b/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java @@ -7,13 +7,13 @@ import javafx.scene.paint.Color; import javafx.scene.text.Text; +import org.jabref.gui.icon.JabRefMaterialDesignIconFactory; import org.jabref.gui.util.BaseDialog; import org.jabref.gui.util.ValueTableCellFactory; import org.jabref.logic.l10n.Localization; import com.airhacks.afterburner.views.ViewLoader; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; -import de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory; public class CopyFilesDialogView extends BaseDialog { @@ -48,7 +48,7 @@ private void setupTable() { colFile.setCellFactory(new ValueTableCellFactory().withText(item -> item).withTooltip(item -> item)); colStatus.setCellFactory(new ValueTableCellFactory().withGraphic(item -> { - Text icon = MaterialDesignIconFactory.get().createIcon(item); + Text icon = JabRefMaterialDesignIconFactory.get().createIcon(item); if (item == MaterialDesignIcon.CHECK) { icon.setFill(Color.GREEN); } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java index 725c206ffa6..ddd420367b3 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java @@ -30,6 +30,7 @@ import org.jabref.gui.DragAndDropDataFormats; import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.copyfiles.CopySingleFileAction; +import org.jabref.gui.icon.JabRefMaterialDesignIconFactory; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.util.TaskExecutor; import org.jabref.gui.util.ViewModelListCellFactory; @@ -43,7 +44,6 @@ import com.airhacks.afterburner.views.ViewLoader; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; -import de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory; public class LinkedFilesEditor extends HBox implements FieldEditorFX { @@ -142,13 +142,13 @@ private static Node createFileDisplay(LinkedFileViewModel linkedFile) { info.setStyle("-fx-padding: 0.5em 0 0.5em 0;"); // To align with buttons below which also have 0.5em padding info.getChildren().setAll(icon, link, desc, progressIndicator); - Button acceptAutoLinkedFile = MaterialDesignIconFactory.get().createIconButton(MaterialDesignIcon.BRIEFCASE_CHECK); + Button acceptAutoLinkedFile = JabRefMaterialDesignIconFactory.get().createIconButton(MaterialDesignIcon.BRIEFCASE_CHECK); acceptAutoLinkedFile.setTooltip(new Tooltip(Localization.lang("This file was found automatically. Do you want to link it to this entry?"))); acceptAutoLinkedFile.visibleProperty().bind(linkedFile.isAutomaticallyFoundProperty()); acceptAutoLinkedFile.setOnAction(event -> linkedFile.acceptAsLinked()); acceptAutoLinkedFile.getStyleClass().setAll("icon-button"); - Button writeXMPMetadata = MaterialDesignIconFactory.get().createIconButton(MaterialDesignIcon.IMPORT); + Button writeXMPMetadata = JabRefMaterialDesignIconFactory.get().createIconButton(MaterialDesignIcon.IMPORT); writeXMPMetadata.setTooltip(new Tooltip(Localization.lang("Write BibTeXEntry as XMP-metadata to PDF."))); writeXMPMetadata.visibleProperty().bind(linkedFile.canWriteXMPMetadataProperty()); writeXMPMetadata.setOnAction(event -> linkedFile.writeXMPMetadata()); diff --git a/src/main/java/org/jabref/gui/icon/IconTheme.java b/src/main/java/org/jabref/gui/icon/IconTheme.java index 131292628cf..44a54608fe3 100644 --- a/src/main/java/org/jabref/gui/icon/IconTheme.java +++ b/src/main/java/org/jabref/gui/icon/IconTheme.java @@ -209,6 +209,7 @@ public enum JabRefIcons implements JabRefIcon { EXPORT_TO_CLIPBOARD(MaterialDesignIcon.CLIPBOARD_ARROW_LEFT) /*css: clipboard-arrow-left */, ATTACH_FILE(MaterialDesignIcon.PAPERCLIP) /*css: paperclip*/, AUTO_FILE_LINK(MaterialDesignIcon.FILE_FIND) /*css: file-find */, + AUTO_LINKED_FILE(MaterialDesignIcon.BRIEFCASE_CHECK) /*css: briefcase-check */, QUALITY_ASSURED(MaterialDesignIcon.CERTIFICATE), /*css: certificate */ QUALITY(MaterialDesignIcon.CERTIFICATE), /*css: certificate */ OPEN(MaterialDesignIcon.FOLDER_OUTLINE) /*css: folder */, diff --git a/src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIconFactory.java b/src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIconFactory.java new file mode 100644 index 00000000000..22d0047ac8d --- /dev/null +++ b/src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIconFactory.java @@ -0,0 +1,51 @@ +package org.jabref.gui.icon; + +import javafx.scene.control.Button; +import javafx.scene.text.Text; + +import de.jensd.fx.glyphs.GlyphIcon; +import de.jensd.fx.glyphs.GlyphIcons; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView; + +/** + Custom Factory class as a workaround for using de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory because of the following issue: https://github.com/JabRef/jabref/issues/5245 + If fixed, use de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory again and delete this class + */ +public class JabRefMaterialDesignIconFactory { + + private static JabRefMaterialDesignIconFactory me; + + private JabRefMaterialDesignIconFactory() { } + + public static JabRefMaterialDesignIconFactory get() { + if (me == null) { + me = new JabRefMaterialDesignIconFactory(); + } + return me; + } + + public Button createIconButton(GlyphIcons icon) { + Text label = createIcon(icon, GlyphIcon.DEFAULT_FONT_SIZE); + Button button = new Button(); + button.setGraphic(label); + return button; + } + + public Text createIcon(GlyphIcons icon) { + return createIcon(icon, GlyphIcon.DEFAULT_FONT_SIZE); + } + + public Text createIcon(GlyphIcons icon, String iconSize) { + if (icon instanceof MaterialDesignIcon) { + // workaround for not using MaterialDesignIconFactory + return new MaterialDesignIconView((MaterialDesignIcon) icon, iconSize); + } else { + // default case copied from GlyphsFactory + Text text = new Text(icon.unicode()); + text.getStyleClass().add("glyph-icon"); + text.setStyle(String.format("-fx-font-family: %s; -fx-font-size: %s;", icon.fontFamily(), iconSize)); + return text; + } + } +} diff --git a/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java b/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java index 87b9120b69b..e73d3beff79 100644 --- a/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java +++ b/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java @@ -18,10 +18,10 @@ import javafx.scene.text.Text; import javafx.util.Callback; +import org.jabref.gui.icon.JabRefMaterialDesignIconFactory; import org.jabref.model.strings.StringUtil; import de.jensd.fx.glyphs.GlyphIcons; -import de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory; /** * Constructs a {@link ListCell} based on the view model of the row and a bunch of specified converter methods. @@ -57,7 +57,7 @@ public ViewModelListCellFactory withIcon(Callback toIcon) { this.toGraphic = viewModel -> { GlyphIcons icon = toIcon.call(viewModel); if (icon != null) { - return MaterialDesignIconFactory.get().createIcon(icon); + return JabRefMaterialDesignIconFactory.get().createIcon(icon); } return null; }; @@ -66,7 +66,7 @@ public ViewModelListCellFactory withIcon(Callback toIcon) { public ViewModelListCellFactory withIcon(Callback toIcon, Callback toColor) { this.toGraphic = viewModel -> { - Text graphic = MaterialDesignIconFactory.get().createIcon(toIcon.call(viewModel)); + Text graphic = JabRefMaterialDesignIconFactory.get().createIcon(toIcon.call(viewModel)); graphic.setFill(toColor.call(viewModel)); return graphic; }; From 8aaa3c94016b143ab027a0b19546133a25d2dba1 Mon Sep 17 00:00:00 2001 From: Robin Lichtenthaeler Date: Thu, 29 Aug 2019 11:42:35 +0200 Subject: [PATCH 031/560] added all used icons to IconTheme --- src/main/java/org/jabref/gui/icon/IconTheme.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/icon/IconTheme.java b/src/main/java/org/jabref/gui/icon/IconTheme.java index 44a54608fe3..4d46326ff0d 100644 --- a/src/main/java/org/jabref/gui/icon/IconTheme.java +++ b/src/main/java/org/jabref/gui/icon/IconTheme.java @@ -252,6 +252,7 @@ public enum JabRefIcons implements JabRefIcon { FIND_DUPLICATES(MaterialDesignIcon.CODE_EQUAL), /*css: code-equal */ CONNECT_DB(MaterialDesignIcon.CLOUD_UPLOAD), /*cloud-upload*/ SUCCESS(MaterialDesignIcon.CHECK_CIRCLE), + CHECK(MaterialDesignIcon.CHECK) /*css: check */, WARNING(MaterialDesignIcon.ALERT), ERROR(MaterialDesignIcon.ALERT_CIRCLE), CASE_SENSITIVE(MaterialDesignIcon.ALPHABETICAL), /* css: mdi-alphabetical */ @@ -293,7 +294,8 @@ public enum JabRefIcons implements JabRefIcon { LATEX_FILE(MaterialDesignIcon.FILE_OUTLINE), LATEX_COMMENT(MaterialDesignIcon.COMMENT_TEXT_OUTLINE), LATEX_LINE(MaterialDesignIcon.FORMAT_LINE_SPACING), - PASSWORD_REVEALED(MaterialDesignIcon.EYE); + PASSWORD_REVEALED(MaterialDesignIcon.EYE), + ALL_ENTRIES_GROUP_DEFAULT_ICON(MaterialDesignIcon.DATABASE); private final JabRefIcon icon; From 2b760cddf53cceae54fd149774226461f5290daa Mon Sep 17 00:00:00 2001 From: Robin Lichtenthaeler Date: Thu, 29 Aug 2019 15:17:26 +0200 Subject: [PATCH 032/560] capsulated all usages of MaterialDesignIcon in gui/icons and removed IconFactory again --- .../gui/copyfiles/CopyFilesDialogView.java | 21 ++++---- .../CopyFilesResultItemViewModel.java | 9 ++-- .../gui/fieldeditors/LinkedFilesEditor.java | 7 ++- .../jabref/gui/groups/GroupNodeViewModel.java | 3 +- .../java/org/jabref/gui/icon/IconTheme.java | 6 +-- .../icon/JabRefMaterialDesignIconFactory.java | 51 ------------------- .../gui/util/ViewModelListCellFactory.java | 20 +++----- .../logic/groups/DefaultGroupsFactory.java | 7 +-- 8 files changed, 30 insertions(+), 94 deletions(-) delete mode 100644 src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIconFactory.java diff --git a/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java b/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java index a0b5cc53d57..e8d6d20f859 100644 --- a/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java +++ b/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java @@ -5,20 +5,19 @@ import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.paint.Color; -import javafx.scene.text.Text; -import org.jabref.gui.icon.JabRefMaterialDesignIconFactory; +import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.icon.JabRefIcon; import org.jabref.gui.util.BaseDialog; import org.jabref.gui.util.ValueTableCellFactory; import org.jabref.logic.l10n.Localization; import com.airhacks.afterburner.views.ViewLoader; -import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; public class CopyFilesDialogView extends BaseDialog { @FXML private TableView tvResult; - @FXML private TableColumn colStatus; + @FXML private TableColumn colStatus; @FXML private TableColumn colMessage; @FXML private TableColumn colFile; private final CopyFilesDialogViewModel viewModel; @@ -46,16 +45,14 @@ private void setupTable() { colStatus.setCellValueFactory(cellData -> cellData.getValue().getIcon()); colFile.setCellFactory(new ValueTableCellFactory().withText(item -> item).withTooltip(item -> item)); - colStatus.setCellFactory(new ValueTableCellFactory().withGraphic(item -> { - - Text icon = JabRefMaterialDesignIconFactory.get().createIcon(item); - if (item == MaterialDesignIcon.CHECK) { - icon.setFill(Color.GREEN); + colStatus.setCellFactory(new ValueTableCellFactory().withGraphic(item -> { + if (item == IconTheme.JabRefIcons.CHECK) { + item = item.withColor(Color.GREEN); } - if (item == MaterialDesignIcon.ALERT) { - icon.setFill(Color.RED); + if (item == IconTheme.JabRefIcons.WARNING) { + item = item.withColor(Color.RED); } - return icon; + return item.getGraphicNode(); })); tvResult.setItems(viewModel.copyFilesResultListProperty()); diff --git a/src/main/java/org/jabref/gui/copyfiles/CopyFilesResultItemViewModel.java b/src/main/java/org/jabref/gui/copyfiles/CopyFilesResultItemViewModel.java index 12fce765c84..e48f0685112 100644 --- a/src/main/java/org/jabref/gui/copyfiles/CopyFilesResultItemViewModel.java +++ b/src/main/java/org/jabref/gui/copyfiles/CopyFilesResultItemViewModel.java @@ -7,19 +7,20 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.icon.JabRefIcon; public class CopyFilesResultItemViewModel { private final StringProperty file = new SimpleStringProperty(""); - private final ObjectProperty icon = new SimpleObjectProperty<>(MaterialDesignIcon.ALERT); + private final ObjectProperty icon = new SimpleObjectProperty<>(IconTheme.JabRefIcons.WARNING); private final StringProperty message = new SimpleStringProperty(""); public CopyFilesResultItemViewModel(Path file, boolean success, String message) { this.file.setValue(file.toString()); this.message.setValue(message); if (success) { - this.icon.setValue(MaterialDesignIcon.CHECK); + this.icon.setValue(IconTheme.JabRefIcons.CHECK); } } @@ -31,7 +32,7 @@ public StringProperty getMessage() { return message; } - public ObjectProperty getIcon() { + public ObjectProperty getIcon() { return icon; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java index ddd420367b3..ccbf0b75d54 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java @@ -30,7 +30,7 @@ import org.jabref.gui.DragAndDropDataFormats; import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.copyfiles.CopySingleFileAction; -import org.jabref.gui.icon.JabRefMaterialDesignIconFactory; +import org.jabref.gui.icon.IconTheme; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.util.TaskExecutor; import org.jabref.gui.util.ViewModelListCellFactory; @@ -43,7 +43,6 @@ import org.jabref.preferences.JabRefPreferences; import com.airhacks.afterburner.views.ViewLoader; -import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; public class LinkedFilesEditor extends HBox implements FieldEditorFX { @@ -142,13 +141,13 @@ private static Node createFileDisplay(LinkedFileViewModel linkedFile) { info.setStyle("-fx-padding: 0.5em 0 0.5em 0;"); // To align with buttons below which also have 0.5em padding info.getChildren().setAll(icon, link, desc, progressIndicator); - Button acceptAutoLinkedFile = JabRefMaterialDesignIconFactory.get().createIconButton(MaterialDesignIcon.BRIEFCASE_CHECK); + Button acceptAutoLinkedFile = IconTheme.JabRefIcons.AUTO_LINKED_FILE.asButton(); acceptAutoLinkedFile.setTooltip(new Tooltip(Localization.lang("This file was found automatically. Do you want to link it to this entry?"))); acceptAutoLinkedFile.visibleProperty().bind(linkedFile.isAutomaticallyFoundProperty()); acceptAutoLinkedFile.setOnAction(event -> linkedFile.acceptAsLinked()); acceptAutoLinkedFile.getStyleClass().setAll("icon-button"); - Button writeXMPMetadata = JabRefMaterialDesignIconFactory.get().createIconButton(MaterialDesignIcon.IMPORT); + Button writeXMPMetadata = IconTheme.JabRefIcons.IMPORT.asButton(); writeXMPMetadata.setTooltip(new Tooltip(Localization.lang("Write BibTeXEntry as XMP-metadata to PDF."))); writeXMPMetadata.visibleProperty().bind(linkedFile.canWriteXMPMetadataProperty()); writeXMPMetadata.setOnAction(event -> linkedFile.writeXMPMetadata()); diff --git a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java index cb17247dd0c..bd44fb53d33 100644 --- a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java @@ -37,7 +37,6 @@ import org.jabref.model.strings.StringUtil; import com.google.common.base.Enums; -import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; import org.fxmisc.easybind.EasyBind; public class GroupNodeViewModel { @@ -200,7 +199,7 @@ private JabRefIcon createDefaultIcon() { } private Optional parseIcon(String iconCode) { - return Enums.getIfPresent(MaterialDesignIcon.class, iconCode.toUpperCase(Locale.ENGLISH)) + return Enums.getIfPresent(IconTheme.JabRefIcons.class, iconCode.toUpperCase(Locale.ENGLISH)) .toJavaUtil() .map(icon -> new InternalMaterialDesignIcon(getColor(), icon)); } diff --git a/src/main/java/org/jabref/gui/icon/IconTheme.java b/src/main/java/org/jabref/gui/icon/IconTheme.java index 4d46326ff0d..98b9a2074e8 100644 --- a/src/main/java/org/jabref/gui/icon/IconTheme.java +++ b/src/main/java/org/jabref/gui/icon/IconTheme.java @@ -18,7 +18,6 @@ import javafx.scene.image.Image; import javafx.scene.paint.Color; -import org.jabref.logic.groups.DefaultGroupsFactory; import org.jabref.preferences.JabRefPreferences; import de.jensd.fx.glyphs.GlyphIcons; @@ -265,7 +264,7 @@ public enum JabRefIcons implements JabRefIcon { DATE_PICKER(MaterialDesignIcon.CALENDAR), /* css: calendar */ DEFAULT_GROUP_ICON_COLORED(MaterialDesignIcon.PLAY), DEFAULT_GROUP_ICON(MaterialDesignIcon.LABEL_OUTLINE), - ALL_ENTRIES_GROUP_ICON(DefaultGroupsFactory.ALL_ENTRIES_GROUP_DEFAULT_ICON), + ALL_ENTRIES_GROUP_ICON(MaterialDesignIcon.DATABASE), IMPORT(MaterialDesignIcon.CALL_RECEIVED), EXPORT(MaterialDesignIcon.CALL_MADE), PREVIOUS_LEFT(MaterialDesignIcon.CHEVRON_LEFT), @@ -294,8 +293,7 @@ public enum JabRefIcons implements JabRefIcon { LATEX_FILE(MaterialDesignIcon.FILE_OUTLINE), LATEX_COMMENT(MaterialDesignIcon.COMMENT_TEXT_OUTLINE), LATEX_LINE(MaterialDesignIcon.FORMAT_LINE_SPACING), - PASSWORD_REVEALED(MaterialDesignIcon.EYE), - ALL_ENTRIES_GROUP_DEFAULT_ICON(MaterialDesignIcon.DATABASE); + PASSWORD_REVEALED(MaterialDesignIcon.EYE); private final JabRefIcon icon; diff --git a/src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIconFactory.java b/src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIconFactory.java deleted file mode 100644 index 22d0047ac8d..00000000000 --- a/src/main/java/org/jabref/gui/icon/JabRefMaterialDesignIconFactory.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.jabref.gui.icon; - -import javafx.scene.control.Button; -import javafx.scene.text.Text; - -import de.jensd.fx.glyphs.GlyphIcon; -import de.jensd.fx.glyphs.GlyphIcons; -import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; -import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView; - -/** - Custom Factory class as a workaround for using de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory because of the following issue: https://github.com/JabRef/jabref/issues/5245 - If fixed, use de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory again and delete this class - */ -public class JabRefMaterialDesignIconFactory { - - private static JabRefMaterialDesignIconFactory me; - - private JabRefMaterialDesignIconFactory() { } - - public static JabRefMaterialDesignIconFactory get() { - if (me == null) { - me = new JabRefMaterialDesignIconFactory(); - } - return me; - } - - public Button createIconButton(GlyphIcons icon) { - Text label = createIcon(icon, GlyphIcon.DEFAULT_FONT_SIZE); - Button button = new Button(); - button.setGraphic(label); - return button; - } - - public Text createIcon(GlyphIcons icon) { - return createIcon(icon, GlyphIcon.DEFAULT_FONT_SIZE); - } - - public Text createIcon(GlyphIcons icon, String iconSize) { - if (icon instanceof MaterialDesignIcon) { - // workaround for not using MaterialDesignIconFactory - return new MaterialDesignIconView((MaterialDesignIcon) icon, iconSize); - } else { - // default case copied from GlyphsFactory - Text text = new Text(icon.unicode()); - text.getStyleClass().add("glyph-icon"); - text.setStyle(String.format("-fx-font-family: %s; -fx-font-size: %s;", icon.fontFamily(), iconSize)); - return text; - } - } -} diff --git a/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java b/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java index e73d3beff79..c0ce703dee0 100644 --- a/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java +++ b/src/main/java/org/jabref/gui/util/ViewModelListCellFactory.java @@ -14,15 +14,12 @@ import javafx.scene.control.Tooltip; import javafx.scene.input.DragEvent; import javafx.scene.input.MouseEvent; -import javafx.scene.paint.Paint; -import javafx.scene.text.Text; +import javafx.scene.paint.Color; import javafx.util.Callback; -import org.jabref.gui.icon.JabRefMaterialDesignIconFactory; +import org.jabref.gui.icon.JabRefIcon; import org.jabref.model.strings.StringUtil; -import de.jensd.fx.glyphs.GlyphIcons; - /** * Constructs a {@link ListCell} based on the view model of the row and a bunch of specified converter methods. * @@ -53,22 +50,21 @@ public ViewModelListCellFactory withGraphic(Callback toGraphic) { return this; } - public ViewModelListCellFactory withIcon(Callback toIcon) { + public ViewModelListCellFactory withIcon(Callback toIcon) { this.toGraphic = viewModel -> { - GlyphIcons icon = toIcon.call(viewModel); + JabRefIcon icon = toIcon.call(viewModel); if (icon != null) { - return JabRefMaterialDesignIconFactory.get().createIcon(icon); + return icon.getGraphicNode(); } return null; }; return this; } - public ViewModelListCellFactory withIcon(Callback toIcon, Callback toColor) { + public ViewModelListCellFactory withIcon(Callback toIcon, Callback toColor) { this.toGraphic = viewModel -> { - Text graphic = JabRefMaterialDesignIconFactory.get().createIcon(toIcon.call(viewModel)); - graphic.setFill(toColor.call(viewModel)); - return graphic; + + return toIcon.call(viewModel).withColor(toColor.call(viewModel)).getGraphicNode(); }; return this; } diff --git a/src/main/java/org/jabref/logic/groups/DefaultGroupsFactory.java b/src/main/java/org/jabref/logic/groups/DefaultGroupsFactory.java index ef085adae08..525d716d20a 100644 --- a/src/main/java/org/jabref/logic/groups/DefaultGroupsFactory.java +++ b/src/main/java/org/jabref/logic/groups/DefaultGroupsFactory.java @@ -1,20 +1,17 @@ package org.jabref.logic.groups; +import org.jabref.gui.icon.IconTheme; import org.jabref.logic.l10n.Localization; import org.jabref.model.groups.AllEntriesGroup; -import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; - public class DefaultGroupsFactory { - public static MaterialDesignIcon ALL_ENTRIES_GROUP_DEFAULT_ICON = MaterialDesignIcon.DATABASE; - private DefaultGroupsFactory() { } public static AllEntriesGroup getAllEntriesGroup() { AllEntriesGroup group = new AllEntriesGroup(Localization.lang("All entries")); - group.setIconName(ALL_ENTRIES_GROUP_DEFAULT_ICON.name()); + group.setIconName(IconTheme.JabRefIcons.ALL_ENTRIES_GROUP_ICON.name()); return group; } } From 047cbef3e5c1ec1ee20d1d11d231fd8c824f9179 Mon Sep 17 00:00:00 2001 From: Robin Lichtenthaeler Date: Thu, 29 Aug 2019 15:51:50 +0200 Subject: [PATCH 033/560] fixed dependency on gui and added archUnitTest --- .../java/org/jabref/logic/groups/DefaultGroupsFactory.java | 5 +++-- .../architecture/MainArchitectureTestsWithArchUnit.java | 7 ++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/logic/groups/DefaultGroupsFactory.java b/src/main/java/org/jabref/logic/groups/DefaultGroupsFactory.java index 525d716d20a..f544aff90d7 100644 --- a/src/main/java/org/jabref/logic/groups/DefaultGroupsFactory.java +++ b/src/main/java/org/jabref/logic/groups/DefaultGroupsFactory.java @@ -1,17 +1,18 @@ package org.jabref.logic.groups; -import org.jabref.gui.icon.IconTheme; import org.jabref.logic.l10n.Localization; import org.jabref.model.groups.AllEntriesGroup; public class DefaultGroupsFactory { + private static String ALL_ENTRIES_GROUP_DEFAULT_ICON = "ALL_ENTRIES_GROUP_ICON"; + private DefaultGroupsFactory() { } public static AllEntriesGroup getAllEntriesGroup() { AllEntriesGroup group = new AllEntriesGroup(Localization.lang("All entries")); - group.setIconName(IconTheme.JabRefIcons.ALL_ENTRIES_GROUP_ICON.name()); + group.setIconName(ALL_ENTRIES_GROUP_DEFAULT_ICON); return group; } } diff --git a/src/test/java/org/jabref/architecture/MainArchitectureTestsWithArchUnit.java b/src/test/java/org/jabref/architecture/MainArchitectureTestsWithArchUnit.java index cd56a07e905..dc7308e77f4 100644 --- a/src/test/java/org/jabref/architecture/MainArchitectureTestsWithArchUnit.java +++ b/src/test/java/org/jabref/architecture/MainArchitectureTestsWithArchUnit.java @@ -41,7 +41,12 @@ public static void doNotUseJGoodies(JavaClasses classes) { public static void doNotUseGlazedLists(JavaClasses classes) { noClasses().should().accessClassesThat().resideInAPackage("ca.odell.glazedlists..").check(classes); } - + + @ArchTest + public static void doNotUseGlyphsDirectly(JavaClasses classes) { + noClasses().that().resideOutsideOfPackage("org.jabref.gui.icon").should().accessClassesThat().resideInAnyPackage("de.jensd.fx.glyphs", "de.jensd.fx.glyphs.materialdesignicons").check(classes); + } + //"Currently disabled as there is no alternative for the rest of classes who need awt" @ArchIgnore @ArchTest From d1f55e9fbecc02bcc90a20900bbbd29d27a10f5e Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Sun, 1 Sep 2019 18:04:41 +0200 Subject: [PATCH 034/560] first part of java 11 fix uncomment java version checker, does not work inside eclipse TODO: Write up or generate the classpath entries with add opens as well TODO: mark jmh as test resource folder --- eclipse.gradle | 32 ++++++++++++++++++++---- src/main/java/org/jabref/JabRefMain.java | 2 +- src/main/resources/csl-locales | 2 +- src/main/resources/csl-styles | 2 +- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/eclipse.gradle b/eclipse.gradle index 6fa381b3fe7..8f3aaa17d00 100644 --- a/eclipse.gradle +++ b/eclipse.gradle @@ -11,17 +11,39 @@ eclipseJdt.doLast { f.append('encoding/=UTF-8') } eclipse { -//add libraries to module path: https://github.com/eclipse/buildship/issues/620#issuecomment-390469550 - classpath { + project { + natures 'org.eclipse.buildship.core.gradleprojectnature' + } + + classpath { file { whenMerged { - entries.findAll { - it.kind == 'src' || it.kind == 'lib' - }.each { it.entryAttributes['module'] = 'true' } + entries.findAll { isModule(it) }.each { //(1) + it.entryAttributes['module'] = 'true' + } + + entries.findAll { isSource(it) && isTestScope(it) }.each { + it.entryAttributes['test'] = 'true' + } + + entries.findAll { isLibrary(it) && isTestScope(it) }.each { + it.entryAttributes['test'] = 'true' + } } } + + defaultOutputDir = file('build') + downloadSources = true + downloadJavadoc = true } } + +boolean isLibrary(entry) { return entry.properties.kind.equals('lib') } +boolean isTestScope(entry) { return entry.entryAttributes.get('gradle_used_by_scope').equals('test'); } +boolean isModule(entry) { isLibrary(entry) && !isTestScope(entry); } +boolean isSource(entry) { return entry.properties.kind.equals('src'); } + + // add formatter and cleanup settings to Eclipse settings // see http://stackoverflow.com/a/27461890/873282 diff --git a/src/main/java/org/jabref/JabRefMain.java b/src/main/java/org/jabref/JabRefMain.java index 72051540247..93a16422b46 100644 --- a/src/main/java/org/jabref/JabRefMain.java +++ b/src/main/java/org/jabref/JabRefMain.java @@ -48,7 +48,7 @@ public static void main(String[] args) { public void start(Stage mainStage) throws Exception { try { // Fail on unsupported Java versions - ensureCorrectJavaVersion(); + // ensureCorrectJavaVersion(); FallbackExceptionHandler.installExceptionHandler(); // Init preferences diff --git a/src/main/resources/csl-locales b/src/main/resources/csl-locales index e89e6b08b5c..29ed2ff4328 160000 --- a/src/main/resources/csl-locales +++ b/src/main/resources/csl-locales @@ -1 +1 @@ -Subproject commit e89e6b08b5c621a414fc7114f2129efac5f8c7d5 +Subproject commit 29ed2ff43284f726f9f583981650a86ffb9b236f diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index f5a731144d4..6ed87f55fd7 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit f5a731144d4b0a838e66ce60cd62a92f7a9e66df +Subproject commit 6ed87f55fd75fcecbf8f6b8a96d5edd0c935702e From 041027787e75cad999351ed3366b3ed5f7852cb3 Mon Sep 17 00:00:00 2001 From: Mihu Date: Sun, 1 Sep 2019 22:01:02 +0200 Subject: [PATCH 035/560] add Software entry type to Biblatex and Bibtex --- .../types/BiblatexEntryTypeDefinitions.java | 16 +++++++++++++++- .../entry/types/BibtexEntryTypeDefinitions.java | 10 +++++++++- .../model/entry/types/StandardEntryType.java | 5 ++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java index 1e50af39b65..3ef3a39cb64 100644 --- a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java @@ -414,8 +414,22 @@ public class BiblatexEntryTypeDefinitions { .withRequiredFields(ONLINE.getRequiredFields()) .build(); + private static final BibEntryType SOFTWARE = new BibEntryTypeBuilder() + .withType(StandardEntryType.Software) + .withImportantFields( + StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.HOWPUBLISHED, StandardField.LOCATION, StandardField.DOI, + StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) + .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.HOWPUBLISHED, + StandardField.TYPE, StandardField.VERSION, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.LOCATION, + StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, + StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .build(); + + + public static final List ALL = Arrays.asList(ARTICLE, BOOK, MVBOOK, INBOOK, BOOKINBOOK, SUPPBOOK, BOOKLET, COLLECTION, MVCOLLECTION, INCOLLECTION, SUPPCOLLECTION, MANUAL, MISC, ONLINE, PATENT, PERIODICAL, SUPPPERIODICAL, PROCEEDINGS, MVPROCEEDINGS, INPROCEEDINGS, REFERENCE, MVREFERENCE, INREFERENCE, REPORT, SET, - THESIS, UNPUBLISHED, CONFERENCE, ELECTRONIC, MASTERSTHESIS, PHDTHESIS, TECHREPORT, WWW); + THESIS, UNPUBLISHED, CONFERENCE, ELECTRONIC, MASTERSTHESIS, PHDTHESIS, TECHREPORT, WWW, SOFTWARE); } diff --git a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java index 068db83100f..a905da816dc 100644 --- a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java @@ -177,8 +177,16 @@ public class BibtexEntryTypeDefinitions { .withImportantFields(StandardField.MONTH, StandardField.YEAR) .build(); + private static final BibEntryType SOFTWARE = new BibEntryTypeBuilder() + .withType(StandardEntryType.Software) + .withImportantFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.HOWPUBLISHED, StandardField.MONTH, StandardField.YEAR, StandardField.NOTE) + .build(); + + + + public static final List ALL = Arrays.asList(ARTICLE, INBOOK, BOOK, BOOKLET, INCOLLECTION, CONFERENCE, - INPROCEEDINGS, PROCEEDINGS, MANUAL, MASTERSTHESIS, PHDTHESIS, TECHREPORT, UNPUBLISHED, MISC); + INPROCEEDINGS, PROCEEDINGS, MANUAL, MASTERSTHESIS, PHDTHESIS, TECHREPORT, UNPUBLISHED, MISC, SOFTWARE); private BibtexEntryTypeDefinitions() { } diff --git a/src/main/java/org/jabref/model/entry/types/StandardEntryType.java b/src/main/java/org/jabref/model/entry/types/StandardEntryType.java index de5ef6dc8fc..9898106469e 100644 --- a/src/main/java/org/jabref/model/entry/types/StandardEntryType.java +++ b/src/main/java/org/jabref/model/entry/types/StandardEntryType.java @@ -34,7 +34,10 @@ public enum StandardEntryType implements EntryType { SuppCollection("SuppCollection"), SuppPeriodical("SuppPeriodical"), Thesis("Thesis"), - WWW("WWW"); + WWW("WWW"), + Software("Software"); + + private final String displayName; From 6e8ec1cb6277bc6200dc0f0ef250e9fdbbe02975 Mon Sep 17 00:00:00 2001 From: Mihu Date: Sun, 1 Sep 2019 22:11:32 +0200 Subject: [PATCH 036/560] add DataSet entry type to Biblatex and Bibtex --- .../entry/types/BiblatexEntryTypeDefinitions.java | 14 +++++++++++++- .../entry/types/BibtexEntryTypeDefinitions.java | 8 +++++++- .../model/entry/types/StandardEntryType.java | 3 ++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java index 3ef3a39cb64..d5c57bee73d 100644 --- a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java @@ -426,10 +426,22 @@ public class BiblatexEntryTypeDefinitions { StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) .build(); + private static final BibEntryType DATASET = new BibEntryTypeBuilder() + .withType(StandardEntryType.DATESET) + .withImportantFields( + StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.HOWPUBLISHED, StandardField.LOCATION, StandardField.DOI, + StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) + .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.HOWPUBLISHED, + StandardField.TYPE, StandardField.VERSION, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.LOCATION, + StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, + StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) + .build(); + public static final List ALL = Arrays.asList(ARTICLE, BOOK, MVBOOK, INBOOK, BOOKINBOOK, SUPPBOOK, BOOKLET, COLLECTION, MVCOLLECTION, INCOLLECTION, SUPPCOLLECTION, MANUAL, MISC, ONLINE, PATENT, PERIODICAL, SUPPPERIODICAL, PROCEEDINGS, MVPROCEEDINGS, INPROCEEDINGS, REFERENCE, MVREFERENCE, INREFERENCE, REPORT, SET, - THESIS, UNPUBLISHED, CONFERENCE, ELECTRONIC, MASTERSTHESIS, PHDTHESIS, TECHREPORT, WWW, SOFTWARE); + THESIS, UNPUBLISHED, CONFERENCE, ELECTRONIC, MASTERSTHESIS, PHDTHESIS, TECHREPORT, WWW, SOFTWARE, DATASET); } diff --git a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java index a905da816dc..e03714ca4e5 100644 --- a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java @@ -177,16 +177,22 @@ public class BibtexEntryTypeDefinitions { .withImportantFields(StandardField.MONTH, StandardField.YEAR) .build(); + private static final BibEntryType SOFTWARE = new BibEntryTypeBuilder() .withType(StandardEntryType.Software) .withImportantFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.HOWPUBLISHED, StandardField.MONTH, StandardField.YEAR, StandardField.NOTE) .build(); + private static final BibEntryType DATESET = new BibEntryTypeBuilder() + .withType(StandardEntryType.DATESET) + .withImportantFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.HOWPUBLISHED, StandardField.MONTH, StandardField.YEAR, StandardField.NOTE) + .build(); + public static final List ALL = Arrays.asList(ARTICLE, INBOOK, BOOK, BOOKLET, INCOLLECTION, CONFERENCE, - INPROCEEDINGS, PROCEEDINGS, MANUAL, MASTERSTHESIS, PHDTHESIS, TECHREPORT, UNPUBLISHED, MISC, SOFTWARE); + INPROCEEDINGS, PROCEEDINGS, MANUAL, MASTERSTHESIS, PHDTHESIS, TECHREPORT, UNPUBLISHED, MISC, SOFTWARE, DATESET ); private BibtexEntryTypeDefinitions() { } diff --git a/src/main/java/org/jabref/model/entry/types/StandardEntryType.java b/src/main/java/org/jabref/model/entry/types/StandardEntryType.java index 9898106469e..9b2f87d1674 100644 --- a/src/main/java/org/jabref/model/entry/types/StandardEntryType.java +++ b/src/main/java/org/jabref/model/entry/types/StandardEntryType.java @@ -35,7 +35,8 @@ public enum StandardEntryType implements EntryType { SuppPeriodical("SuppPeriodical"), Thesis("Thesis"), WWW("WWW"), - Software("Software"); + Software("Software"), + DATESET("DataSet"); From c6960b3b78d87b6ec463afa8347ef36f8754b270 Mon Sep 17 00:00:00 2001 From: Mihu Date: Sun, 1 Sep 2019 22:15:43 +0200 Subject: [PATCH 037/560] add eventitle, eventdate and venue to @unpublished entry type --- .../model/entry/types/BiblatexEntryTypeDefinitions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java index d5c57bee73d..84563c58f61 100644 --- a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java @@ -351,8 +351,8 @@ public class BiblatexEntryTypeDefinitions { StandardField.PUBSTATE, StandardField.URL, StandardField.URLDATE) .withRequiredFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.DATE) .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.HOWPUBLISHED, - StandardField.NOTE, StandardField.LOCATION, StandardField.ADDENDUM, StandardField.PUBSTATE, - StandardField.URL, StandardField.URLDATE) + StandardField.NOTE, StandardField.LOCATION, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.EVENTTITLE, + StandardField.EVENTDATE,StandardField.VENUE, StandardField.URL, StandardField.URLDATE) .build(); private static final BibEntryType CONFERENCE = new BibEntryTypeBuilder() From 0c8b8f32293ee8aa5bf4981014a008fc9b574d21 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 07:39:29 +0000 Subject: [PATCH 038/560] Bump jakarta.annotation-api from 1.3.4 to 1.3.5 Bumps [jakarta.annotation-api](https://github.com/eclipse-ee4j/common-annotations-api) from 1.3.4 to 1.3.5. - [Release notes](https://github.com/eclipse-ee4j/common-annotations-api/releases) - [Commits](https://github.com/eclipse-ee4j/common-annotations-api/commits/1.3.5) Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 04ea37d1ef7..de500c8330e 100644 --- a/build.gradle +++ b/build.gradle @@ -155,7 +155,7 @@ dependencies { exclude module: "jsr305" } - compile group: 'jakarta.annotation', name: 'jakarta.annotation-api', version: '1.3.4' + compile group: 'jakarta.annotation', name: 'jakarta.annotation-api', version: '1.3.5' // JavaFX stuff compile 'de.jensd:fontawesomefx-commons:11.0' From f04e84a658e5ffa2b330ee5a3ae72c135f6a73b4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 07:39:31 +0000 Subject: [PATCH 039/560] Bump checkstyle from 8.23 to 8.24 Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 8.23 to 8.24. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-8.23...checkstyle-8.24) Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 04ea37d1ef7..a9af39eb2f7 100644 --- a/build.gradle +++ b/build.gradle @@ -224,7 +224,7 @@ dependencies { testCompile "org.testfx:testfx-core:4.0.15-alpha" testCompile "org.testfx:testfx-junit5:4.0.15-alpha" - checkstyle 'com.puppycrawl.tools:checkstyle:8.23' + checkstyle 'com.puppycrawl.tools:checkstyle:8.24' xjc 'com.sun.xml.bind:jaxb-xjc:2.2.4-1' jython 'org.python:jython-standalone:2.7.1' } From 737f9356bcae78d934c1f9eb503fb0a5cdaff593 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 07:39:33 +0000 Subject: [PATCH 040/560] Bump org.beryx.jlink from 2.15.0 to 2.15.1 Bumps org.beryx.jlink from 2.15.0 to 2.15.1. Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 04ea37d1ef7..7e6a740de01 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ plugins { id 'com.github.ben-manes.versions' version '0.22.0' id 'org.javamodularity.moduleplugin' version '1.5.0' id 'org.openjfx.javafxplugin' version '0.0.8' - id 'org.beryx.jlink' version '2.15.0' + id 'org.beryx.jlink' version '2.15.1' } // use the gradle build scan feature: https://scans.gradle.com/get-started From 09953c4680347a5c3e1204ec23a262c83ad292db Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 07:39:49 +0000 Subject: [PATCH 041/560] Bump guava from 28.0-jre to 28.1-jre Bumps [guava](https://github.com/google/guava) from 28.0-jre to 28.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 04ea37d1ef7..99ede820061 100644 --- a/build.gradle +++ b/build.gradle @@ -150,7 +150,7 @@ dependencies { compile 'org.postgresql:postgresql:42.2.6' - compile ('com.google.guava:guava:28.0-jre') { + compile ('com.google.guava:guava:28.1-jre') { // TODO: Remove this as soon as https://github.com/google/guava/issues/2960 is fixed exclude module: "jsr305" } From 496bb930ad25792e3134b3290f3d5818d7c97338 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 07:39:50 +0000 Subject: [PATCH 042/560] Bump richtextfx from 0.10.1 to 0.10.2 Bumps [richtextfx](https://github.com/FXMisc/RichTextFX) from 0.10.1 to 0.10.2. - [Release notes](https://github.com/FXMisc/RichTextFX/releases) - [Changelog](https://github.com/FXMisc/RichTextFX/blob/master/CHANGELOG.md) - [Commits](https://github.com/FXMisc/RichTextFX/compare/v0.10.1...v0.10.2) Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 04ea37d1ef7..2030ca4a48b 100644 --- a/build.gradle +++ b/build.gradle @@ -164,7 +164,7 @@ dependencies { compile 'de.saxsys:mvvmfx:1.8.0' compile 'org.fxmisc.easybind:easybind:1.0.3' compile 'org.fxmisc.flowless:flowless:0.6.1' - compile 'org.fxmisc.richtext:richtextfx:0.10.1' + compile 'org.fxmisc.richtext:richtextfx:0.10.2' compile group: 'org.glassfish.hk2.external', name: 'jakarta.inject', version: '2.6.1' compile 'com.jfoenix:jfoenix:9.0.9' compile 'org.controlsfx:controlsfx:11.0.0' From 57875249a59adcba5144df2bd9340590159cbfff Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 09:10:50 +0000 Subject: [PATCH 043/560] Bump src/main/resources/csl-styles from `f5a7311` to `fa125ec` Bumps [src/main/resources/csl-styles](https://github.com/citation-style-language/styles) from `f5a7311` to `fa125ec`. - [Release notes](https://github.com/citation-style-language/styles/releases) - [Commits](https://github.com/citation-style-language/styles/compare/f5a731144d4b0a838e66ce60cd62a92f7a9e66df...fa125ec03c6270dea5881ec1623f1b741da6e759) Signed-off-by: dependabot-preview[bot] --- src/main/resources/csl-styles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index f5a731144d4..fa125ec03c6 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit f5a731144d4b0a838e66ce60cd62a92f7a9e66df +Subproject commit fa125ec03c6270dea5881ec1623f1b741da6e759 From 853c4fd15d516b067a6cd8c19936d1ed4559d32c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 12:47:12 +0200 Subject: [PATCH 044/560] Bump com.github.ben-manes.versions from 0.22.0 to 0.24.0 (#5272) Bumps com.github.ben-manes.versions from 0.22.0 to 0.24.0. Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1130c8b7a88..6a4ceddb09d 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ plugins { id "com.simonharrer.modernizer" version '1.8.0-1' id 'me.champeau.gradle.jmh' version '0.4.8' //id 'net.ltgt.errorprone' version '0.8.1' - id 'com.github.ben-manes.versions' version '0.22.0' + id 'com.github.ben-manes.versions' version '0.24.0' id 'org.javamodularity.moduleplugin' version '1.5.0' id 'org.openjfx.javafxplugin' version '0.0.8' id 'org.beryx.jlink' version '2.15.1' From 85b5492e937824688abc26c148b9aceed9c049b2 Mon Sep 17 00:00:00 2001 From: Mihu Date: Mon, 2 Sep 2019 18:35:22 +0200 Subject: [PATCH 045/560] remove @software and @dataset entry type from the bibtex and update changelog --- CHANGELOG.md | 3 ++- .../entry/types/BiblatexEntryTypeDefinitions.java | 2 +- .../entry/types/BibtexEntryTypeDefinitions.java | 15 +-------------- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c9853c1f24..dc47b710dd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,7 +70,8 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# ## [5.0-alpha] – 2019-08-25 ### Changed - +- We added eventitle, eventdate and venue fields to @unpublished entry type. +- We added @software and @dataSet entry type to biblatex. - All fields are now properly sorted alphabetically (in the subgroups of required/optional fields) when the entry is written to the bib file. - We fixed an issue where some importers used the field `pubstatus` instead of the standard BibTeX field `pubstate`. - We changed the latex command removal for docbook exporter. [#3838](https://github.com/JabRef/jabref/issues/3838) diff --git a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java index 84563c58f61..9344c2faff4 100644 --- a/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BiblatexEntryTypeDefinitions.java @@ -432,7 +432,7 @@ public class BiblatexEntryTypeDefinitions { StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.HOWPUBLISHED, StandardField.LOCATION, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) .withRequiredFields(new OrFields(StandardField.AUTHOR, StandardField.EDITOR), StandardField.TITLE, StandardField.DATE) - .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.HOWPUBLISHED, + .withDetailFields(StandardField.SUBTITLE, StandardField.TITLEADDON, StandardField.LANGUAGE, StandardField.EDITION, StandardField.HOWPUBLISHED, StandardField.TYPE, StandardField.VERSION, StandardField.NOTE, StandardField.ORGANIZATION, StandardField.LOCATION, StandardField.ADDENDUM, StandardField.PUBSTATE, StandardField.DOI, StandardField.EPRINT, StandardField.EPRINTCLASS, StandardField.EPRINTTYPE, StandardField.URL, StandardField.URLDATE) diff --git a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java index e03714ca4e5..d02f9f65b9a 100644 --- a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java @@ -178,21 +178,8 @@ public class BibtexEntryTypeDefinitions { .build(); - private static final BibEntryType SOFTWARE = new BibEntryTypeBuilder() - .withType(StandardEntryType.Software) - .withImportantFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.HOWPUBLISHED, StandardField.MONTH, StandardField.YEAR, StandardField.NOTE) - .build(); - - private static final BibEntryType DATESET = new BibEntryTypeBuilder() - .withType(StandardEntryType.DATESET) - .withImportantFields(StandardField.AUTHOR, StandardField.TITLE, StandardField.HOWPUBLISHED, StandardField.MONTH, StandardField.YEAR, StandardField.NOTE) - .build(); - - - - public static final List ALL = Arrays.asList(ARTICLE, INBOOK, BOOK, BOOKLET, INCOLLECTION, CONFERENCE, - INPROCEEDINGS, PROCEEDINGS, MANUAL, MASTERSTHESIS, PHDTHESIS, TECHREPORT, UNPUBLISHED, MISC, SOFTWARE, DATESET ); + INPROCEEDINGS, PROCEEDINGS, MANUAL, MASTERSTHESIS, PHDTHESIS, TECHREPORT, UNPUBLISHED, MISC ); private BibtexEntryTypeDefinitions() { } From 1a70233631d3f3d4b71e900ccb521d6af31ccb41 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 3 Sep 2019 11:26:37 +0200 Subject: [PATCH 046/560] Fix spacing --- .../jabref/model/entry/types/BibtexEntryTypeDefinitions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java index d02f9f65b9a..f4c5dfc2df7 100644 --- a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java @@ -179,7 +179,7 @@ public class BibtexEntryTypeDefinitions { public static final List ALL = Arrays.asList(ARTICLE, INBOOK, BOOK, BOOKLET, INCOLLECTION, CONFERENCE, - INPROCEEDINGS, PROCEEDINGS, MANUAL, MASTERSTHESIS, PHDTHESIS, TECHREPORT, UNPUBLISHED, MISC ); + INPROCEEDINGS, PROCEEDINGS, MANUAL, MASTERSTHESIS, PHDTHESIS, TECHREPORT, UNPUBLISHED, MISC); private BibtexEntryTypeDefinitions() { } From 305223565f31c412a4632148adec669b9a1d20cc Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 3 Sep 2019 11:26:55 +0200 Subject: [PATCH 047/560] Fix empty line --- .../org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java index f4c5dfc2df7..068db83100f 100644 --- a/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java +++ b/src/main/java/org/jabref/model/entry/types/BibtexEntryTypeDefinitions.java @@ -177,7 +177,6 @@ public class BibtexEntryTypeDefinitions { .withImportantFields(StandardField.MONTH, StandardField.YEAR) .build(); - public static final List ALL = Arrays.asList(ARTICLE, INBOOK, BOOK, BOOKLET, INCOLLECTION, CONFERENCE, INPROCEEDINGS, PROCEEDINGS, MANUAL, MASTERSTHESIS, PHDTHESIS, TECHREPORT, UNPUBLISHED, MISC); From 9aa68a531de4eb3b594008d7524d43d04bbca80d Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 4 Sep 2019 12:54:09 +0200 Subject: [PATCH 048/560] added add-opens options (#5279) --- build.gradle | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6a4ceddb09d..47c57be802e 100644 --- a/build.gradle +++ b/build.gradle @@ -409,7 +409,11 @@ run { "--add-exports", "javafx.web/com.sun.webkit=org.controlsfx.controls", "--add-exports", "javafx.graphics/com.sun.javafx.css=org.controlsfx.controls", "--add-opens", "javafx.controls/javafx.scene.control.skin=org.controlsfx.controls", - "--add-opens", "javafx.graphics/javafx.scene=org.controlsfx.controls" + "--add-opens", "javafx.graphics/javafx.scene=org.controlsfx.controls", + "--add-opens", "javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix", + "--add-opens", "javafx.base/com.sun.javafx.binding=com.jfoenix", + "--add-opens", "javafx.graphics/com.sun.javafx.stage=com.jfoenix", + "--add-opens", "javafx.base/com.sun.javafx.event=com.jfoenix" // TODO: The following code should have the same affect as the above one, but doesn't work for some reason // https://github.com/java9-modularity/gradle-modules-plugin/issues/89 From cce50080c0bff7424bd856006371e6ce8e52b531 Mon Sep 17 00:00:00 2001 From: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> Date: Wed, 4 Sep 2019 15:02:17 +0200 Subject: [PATCH 049/560] Conversion of Preferences / XMP and NameFormatters to mvvm (#5265) * Initial * l10n * Added validator and cleanups * Solved review issues * l10n * Removed PropertyValueFactory --- .../preferences/NameFormatterItemModel.java | 45 ++++ .../gui/preferences/NameFormatterTab.fxml | 51 ++++ .../gui/preferences/NameFormatterTab.java | 233 ------------------ .../gui/preferences/NameFormatterTabView.java | 100 ++++++++ .../NameFormatterTabViewModel.java | 87 +++++++ .../PreferencesDialogViewModel.java | 4 +- .../jabref/gui/preferences/XmpPrefsTab.java | 181 -------------- .../jabref/gui/preferences/XmpPrivacyTab.fxml | 45 ++++ .../gui/preferences/XmpPrivacyTabView.java | 111 +++++++++ .../preferences/XmpPrivacyTabViewModel.java | 114 +++++++++ .../java/org/jabref/gui/util/FieldsUtil.java | 55 +++++ src/main/resources/l10n/JabRef_en.properties | 16 +- 12 files changed, 622 insertions(+), 420 deletions(-) create mode 100644 src/main/java/org/jabref/gui/preferences/NameFormatterItemModel.java create mode 100644 src/main/java/org/jabref/gui/preferences/NameFormatterTab.fxml delete mode 100644 src/main/java/org/jabref/gui/preferences/NameFormatterTab.java create mode 100644 src/main/java/org/jabref/gui/preferences/NameFormatterTabView.java create mode 100644 src/main/java/org/jabref/gui/preferences/NameFormatterTabViewModel.java delete mode 100644 src/main/java/org/jabref/gui/preferences/XmpPrefsTab.java create mode 100644 src/main/java/org/jabref/gui/preferences/XmpPrivacyTab.fxml create mode 100644 src/main/java/org/jabref/gui/preferences/XmpPrivacyTabView.java create mode 100644 src/main/java/org/jabref/gui/preferences/XmpPrivacyTabViewModel.java create mode 100644 src/main/java/org/jabref/gui/util/FieldsUtil.java diff --git a/src/main/java/org/jabref/gui/preferences/NameFormatterItemModel.java b/src/main/java/org/jabref/gui/preferences/NameFormatterItemModel.java new file mode 100644 index 00000000000..721407bc3b3 --- /dev/null +++ b/src/main/java/org/jabref/gui/preferences/NameFormatterItemModel.java @@ -0,0 +1,45 @@ +package org.jabref.gui.preferences; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +import org.jabref.logic.layout.format.NameFormatter; + +public class NameFormatterItemModel { + private final StringProperty name = new SimpleStringProperty(""); + private final StringProperty format = new SimpleStringProperty(""); + + NameFormatterItemModel() { this(""); } + + NameFormatterItemModel(String name) { + this(name, NameFormatter.DEFAULT_FORMAT); + } + + NameFormatterItemModel(String name, String format) { + this.name.setValue(name); + this.format.setValue(format); + } + + public void setName(String name) { + this.name.setValue(name); + } + + public String getName() { + return name.getValue(); + } + + public StringProperty nameProperty() { return name; } + + public void setFormat(String format) { + this.format.setValue(format); + } + + public String getFormat() { + return format.getValue(); + } + + public StringProperty formatProperty() { return format; } + + @Override + public String toString() { return "[" + name.getValue() + "," + format.getValue() + "]"; } +} diff --git a/src/main/java/org/jabref/gui/preferences/NameFormatterTab.fxml b/src/main/java/org/jabref/gui/preferences/NameFormatterTab.fxml new file mode 100644 index 00000000000..54cf2632d98 --- /dev/null +++ b/src/main/java/org/jabref/gui/preferences/NameFormatterTab.fxml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + diff --git a/src/main/java/org/jabref/gui/preferences/NameFormatterTab.java b/src/main/java/org/jabref/gui/preferences/NameFormatterTab.java deleted file mode 100644 index 94d7e84c12b..00000000000 --- a/src/main/java/org/jabref/gui/preferences/NameFormatterTab.java +++ /dev/null @@ -1,233 +0,0 @@ -package org.jabref.gui.preferences; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -import javafx.beans.property.SimpleStringProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.scene.Node; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextField; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.control.cell.TextFieldTableCell; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; - -import org.jabref.gui.actions.ActionFactory; -import org.jabref.gui.actions.StandardActions; -import org.jabref.gui.help.HelpAction; -import org.jabref.logic.help.HelpFile; -import org.jabref.logic.l10n.Localization; -import org.jabref.logic.layout.format.NameFormatter; -import org.jabref.preferences.JabRefPreferences; - -public class NameFormatterTab extends Pane implements PreferencesTab { - - private final JabRefPreferences prefs; - private boolean tableChanged; - private final TableView table; - private final GridPane builder = new GridPane(); - private final List tableRows = new ArrayList<>(10); - private final ObservableList data = FXCollections.observableArrayList(); - - public static class NameFormatterViewModel { - - private final SimpleStringProperty name; - private final SimpleStringProperty format; - - NameFormatterViewModel() { - this(""); - } - - NameFormatterViewModel(String name) { - this(name, NameFormatter.DEFAULT_FORMAT); - } - - NameFormatterViewModel(String name, String format) { - this.name = new SimpleStringProperty(name); - this.format = new SimpleStringProperty(format); - } - - public String getName() { - return name.get(); - } - - public void setName(String name) { - this.name.set(name); - } - - public String getFormat() { - return format.get(); - } - - public void setFormat(String format) { - this.format.set(format); - } - } - - /** - * Tab to create custom Name Formatters - * - */ - public NameFormatterTab(JabRefPreferences prefs) { - this.prefs = Objects.requireNonNull(prefs); - - ActionFactory factory = new ActionFactory(prefs.getKeyBindingRepository()); - - TableColumn firstCol = new TableColumn<>(Localization.lang("Formatter name")); - TableColumn lastCol = new TableColumn<>(Localization.lang("Format string")); - table = new TableView<>(); - table.setEditable(true); - firstCol.setCellValueFactory(new PropertyValueFactory<>("name")); - firstCol.setCellFactory(TextFieldTableCell.forTableColumn()); - firstCol.setOnEditCommit( - (TableColumn.CellEditEvent t) -> { - t.getTableView().getItems().get( - t.getTablePosition().getRow()) - .setName(t.getNewValue()); - }); - lastCol.setCellValueFactory(new PropertyValueFactory<>("format")); - lastCol.setCellFactory(TextFieldTableCell.forTableColumn()); - lastCol.setOnEditCommit( - (TableColumn.CellEditEvent t) -> { - t.getTableView().getItems().get( - t.getTablePosition().getRow()) - .setFormat(t.getNewValue()); - }); - firstCol.setPrefWidth(140); - lastCol.setPrefWidth(200); - table.setItems(data); - table.getColumns().addAll(Arrays.asList(firstCol, lastCol)); - final TextField addName = new TextField(); - addName.setPromptText("name"); - addName.setMaxWidth(100); - final TextField addLast = new TextField(); - addLast.setMaxWidth(100); - addLast.setPromptText("format"); - - BorderPane tabPanel = new BorderPane(); - ScrollPane scrollPane = new ScrollPane(); - scrollPane.setMaxHeight(400); - scrollPane.setMaxWidth(360); - scrollPane.setContent(table); - tabPanel.setCenter(scrollPane); - - Label insertRows = new Label(Localization.lang("Insert rows")); - insertRows.setVisible(false); - Button add = new Button("Insert"); - add.setOnAction(e -> { - if (!addName.getText().isEmpty() && !addLast.getText().isEmpty()) { - NameFormatterViewModel tableRow = new NameFormatterViewModel(addName.getText(), addLast.getText()); - addName.clear(); - addLast.clear(); - data.add(tableRow); - tableRows.add(tableRow); - table.setItems(data); - tableChanged = true; - table.refresh(); - } - }); - Label deleteRows = new Label(Localization.lang("Delete rows")); - deleteRows.setVisible(false); - Button delete = new Button("Delete"); - delete.setOnAction(e -> { - if ((table.getFocusModel() != null) && (table.getFocusModel().getFocusedIndex() != -1)) { - tableChanged = true; - int row = table.getFocusModel().getFocusedIndex(); - NameFormatterViewModel tableRow = tableRows.get(row); - tableRows.remove(tableRow); - data.remove(tableRow); - table.setItems(data); - table.refresh(); - } - }); - - Button help = factory.createIconButton(StandardActions.HELP_NAME_FORMATTER, new HelpAction(HelpFile.CUSTOM_EXPORTS_NAME_FORMATTER)); - HBox toolbar = new HBox(); - toolbar.getChildren().addAll(addName, addLast, add, delete, help); - tabPanel.setBottom(toolbar); - - Label specialNameFormatters = new Label(Localization.lang("Special name formatters")); - specialNameFormatters.getStyleClass().add("sectionHeader"); - builder.add(specialNameFormatters, 1, 1); - builder.add(tabPanel, 1, 2); - } - - @Override - public Node getBuilder() { - return builder; - } - - @Override - public void setValues() { - tableRows.clear(); - List names = prefs.getStringList(JabRefPreferences.NAME_FORMATER_KEY); - List formats = prefs.getStringList(JabRefPreferences.NAME_FORMATTER_VALUE); - - for (int i = 0; i < names.size(); i++) { - if (i < formats.size()) { - tableRows.add(new NameFormatterViewModel(names.get(i), formats.get(i))); - } else { - tableRows.add(new NameFormatterViewModel(names.get(i))); - } - } - } - - /** - * Store changes to table preferences. This method is called when the user - * clicks Ok. - * - */ - @Override - public void storeSettings() { - - // Now we need to make sense of the contents the user has made to the - // table setup table. - if (tableChanged) { - // First we remove all rows with empty names. - int i = 0; - while (i < tableRows.size()) { - if (tableRows.get(i).getName().isEmpty()) { - tableRows.remove(i); - } else { - i++; - } - } - // Then we make lists - - List names = new ArrayList<>(tableRows.size()); - List formats = new ArrayList<>(tableRows.size()); - - for (NameFormatterViewModel tr : tableRows) { - names.add(tr.getName()); - formats.add(tr.getFormat()); - } - - // Finally, we store the new preferences. - prefs.putStringList(JabRefPreferences.NAME_FORMATER_KEY, names); - prefs.putStringList(JabRefPreferences.NAME_FORMATTER_VALUE, formats); - } - } - - @Override - public boolean validateSettings() { - return true; - } - - @Override - public String getTabName() { - return Localization.lang("Name formatter"); - } - - @Override - public List getRestartWarnings() { return new ArrayList<>(); } -} diff --git a/src/main/java/org/jabref/gui/preferences/NameFormatterTabView.java b/src/main/java/org/jabref/gui/preferences/NameFormatterTabView.java new file mode 100644 index 00000000000..950c610d24e --- /dev/null +++ b/src/main/java/org/jabref/gui/preferences/NameFormatterTabView.java @@ -0,0 +1,100 @@ +package org.jabref.gui.preferences; + +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.control.cell.TextFieldTableCell; +import javafx.scene.input.KeyCode; + +import org.jabref.Globals; +import org.jabref.gui.actions.ActionFactory; +import org.jabref.gui.actions.StandardActions; +import org.jabref.gui.help.HelpAction; +import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.util.ValueTableCellFactory; +import org.jabref.logic.help.HelpFile; +import org.jabref.logic.l10n.Localization; +import org.jabref.preferences.JabRefPreferences; + +import com.airhacks.afterburner.views.ViewLoader; + +public class NameFormatterTabView extends AbstractPreferenceTabView implements PreferencesTab { + + @FXML private TableView formatterList; + @FXML private TableColumn formatterNameColumn; + @FXML private TableColumn formatterStringColumn; + @FXML private TableColumn actionsColumn; + @FXML private TextField addFormatterName; + @FXML private TextField addFormatterString; + @FXML private Button formatterHelp; + + public NameFormatterTabView(JabRefPreferences preferences) { + this.preferences = preferences; + + ViewLoader.view(this) + .root(this) + .load(); + } + + @Override + public String getTabName() { return Localization.lang("Name formatter"); } + + public void initialize () { + NameFormatterTabViewModel nameFormatterTabViewModel = new NameFormatterTabViewModel(dialogService, preferences); + this.viewModel = nameFormatterTabViewModel; + + formatterList.setEditable(true); + + formatterNameColumn.setSortable(true); + formatterNameColumn.setReorderable(false); + formatterNameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty()); + formatterNameColumn.setCellFactory(TextFieldTableCell.forTableColumn()); + formatterNameColumn.setEditable(true); + formatterNameColumn.setOnEditCommit( + (TableColumn.CellEditEvent event) -> { + event.getRowValue().setName(event.getNewValue()); + }); + + formatterStringColumn.setSortable(true); + formatterStringColumn.setReorderable(false); + formatterStringColumn.setCellValueFactory(cellData -> cellData.getValue().formatProperty()); + formatterStringColumn.setCellFactory(TextFieldTableCell.forTableColumn()); + formatterStringColumn.setEditable(true); + formatterStringColumn.setOnEditCommit( + (TableColumn.CellEditEvent event) -> { + event.getRowValue().setFormat(event.getNewValue()); + }); + + actionsColumn.setSortable(false); + actionsColumn.setReorderable(false); + actionsColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty()); + new ValueTableCellFactory() + .withGraphic(name -> IconTheme.JabRefIcons.DELETE_ENTRY.getGraphicNode()) + .withTooltip(name -> Localization.lang("Remove") + " " + name) + .withOnMouseClickedEvent(item -> evt -> { + nameFormatterTabViewModel.removeFormatter(formatterList.getFocusModel().getFocusedItem()); + }) + .install(actionsColumn); + + formatterList.setOnKeyPressed(event -> { + if (event.getCode() == KeyCode.DELETE) { + nameFormatterTabViewModel.removeFormatter(formatterList.getSelectionModel().getSelectedItem()); + } + }); + + formatterList.itemsProperty().bindBidirectional(nameFormatterTabViewModel.formatterListProperty()); + + addFormatterName.textProperty().bindBidirectional(nameFormatterTabViewModel.addFormatterNameProperty()); + addFormatterString.textProperty().bindBidirectional(nameFormatterTabViewModel.addFormatterStringProperty()); + + ActionFactory actionFactory = new ActionFactory(Globals.getKeyPrefs()); + actionFactory.configureIconButton(StandardActions.HELP_NAME_FORMATTER, new HelpAction(HelpFile.CUSTOM_EXPORTS_NAME_FORMATTER), formatterHelp); + } + + public void addFormatter() { + ((NameFormatterTabViewModel) viewModel).addFormatter(); + } + +} diff --git a/src/main/java/org/jabref/gui/preferences/NameFormatterTabViewModel.java b/src/main/java/org/jabref/gui/preferences/NameFormatterTabViewModel.java new file mode 100644 index 00000000000..2d510bcceb2 --- /dev/null +++ b/src/main/java/org/jabref/gui/preferences/NameFormatterTabViewModel.java @@ -0,0 +1,87 @@ +package org.jabref.gui.preferences; + +import java.util.ArrayList; +import java.util.List; + +import javafx.beans.property.ListProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; + +import org.jabref.gui.DialogService; +import org.jabref.model.strings.StringUtil; +import org.jabref.preferences.JabRefPreferences; + +public class NameFormatterTabViewModel implements PreferenceTabViewModel { + + private final ListProperty formatterListProperty = new SimpleListProperty<>(FXCollections.observableArrayList()); + private final StringProperty addFormatterNameProperty = new SimpleStringProperty(); + private final StringProperty addFormatterStringProperty = new SimpleStringProperty(); + + private final DialogService dialogService; + private final JabRefPreferences preferences; + + NameFormatterTabViewModel(DialogService dialogService, JabRefPreferences preferences) { + this.dialogService = dialogService; + this.preferences = preferences; + setValues(); + } + + @Override + public void setValues() { + formatterListProperty.clear(); + List names = preferences.getStringList(JabRefPreferences.NAME_FORMATER_KEY); + List formats = preferences.getStringList(JabRefPreferences.NAME_FORMATTER_VALUE); + + for (int i = 0; i < names.size(); i++) { + if (i < formats.size()) { + formatterListProperty.add(new NameFormatterItemModel(names.get(i), formats.get(i))); + } else { + formatterListProperty.add(new NameFormatterItemModel(names.get(i))); + } + } + } + + @Override + public void storeSettings() { + formatterListProperty.removeIf(formatter -> formatter.getName().isEmpty()); + + List names = new ArrayList<>(formatterListProperty.size()); + List formats = new ArrayList<>(formatterListProperty.size()); + for (NameFormatterItemModel formatterListItem : formatterListProperty) { + names.add(formatterListItem.getName()); + formats.add(formatterListItem.getFormat()); + } + + preferences.putStringList(JabRefPreferences.NAME_FORMATER_KEY, names); + preferences.putStringList(JabRefPreferences.NAME_FORMATTER_VALUE, formats); + } + + public void addFormatter() { + if (!StringUtil.isNullOrEmpty(addFormatterNameProperty.getValue()) && + !StringUtil.isNullOrEmpty(addFormatterStringProperty.getValue())) { + + NameFormatterItemModel newFormatter = new NameFormatterItemModel( + addFormatterNameProperty.getValue(), addFormatterStringProperty.getValue()); + + addFormatterNameProperty.setValue(""); + addFormatterStringProperty.setValue(""); + formatterListProperty.add(newFormatter); + } + } + + public void removeFormatter(NameFormatterItemModel formatter) { formatterListProperty.remove(formatter); } + + @Override + public boolean validateSettings() { return true; } + + @Override + public List getRestartWarnings() { return new ArrayList<>(); } + + public ListProperty formatterListProperty() { return formatterListProperty; } + + public StringProperty addFormatterNameProperty() { return addFormatterNameProperty; } + + public StringProperty addFormatterStringProperty() { return addFormatterStringProperty; } +} diff --git a/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java b/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java index 653a1e24cd8..d1a64d69c58 100644 --- a/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java @@ -57,8 +57,8 @@ public PreferencesDialogViewModel(DialogService dialogService, TaskExecutor task new BibtexKeyPatternPrefTab(prefs, frame.getCurrentBasePanel()), new ImportSettingsTab(prefs), new ExportSortingPrefsTab(prefs), - new NameFormatterTab(prefs), - new XmpPrefsTab(prefs), + new NameFormatterTabView(prefs), + new XmpPrivacyTabView(prefs), new AdvancedTabView(prefs), new AppearancePrefsTab(dialogService, prefs) ); diff --git a/src/main/java/org/jabref/gui/preferences/XmpPrefsTab.java b/src/main/java/org/jabref/gui/preferences/XmpPrefsTab.java deleted file mode 100644 index d0020403bb4..00000000000 --- a/src/main/java/org/jabref/gui/preferences/XmpPrefsTab.java +++ /dev/null @@ -1,181 +0,0 @@ -package org.jabref.gui.preferences; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import javafx.beans.property.ListProperty; -import javafx.beans.property.SimpleListProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.collections.FXCollections; -import javafx.scene.Node; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableColumn.CellEditEvent; -import javafx.scene.control.TableView; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; - -import org.jabref.gui.icon.IconTheme.JabRefIcons; -import org.jabref.gui.util.ValueTableCellFactory; -import org.jabref.logic.l10n.Localization; -import org.jabref.model.entry.field.Field; -import org.jabref.model.entry.field.FieldFactory; -import org.jabref.model.strings.StringUtil; -import org.jabref.preferences.JabRefPreferences; - -/** - * Preference Tab for XMP. - * - * Allows the user to enable and configure the XMP privacy filter. - */ -class XmpPrefsTab extends Pane implements PreferencesTab { - - private final JabRefPreferences prefs; - private final GridPane builder = new GridPane(); - private final ListProperty fields = new SimpleListProperty<>(FXCollections.observableArrayList()); - private final CheckBox privacyFilterCheckBox = new CheckBox( - Localization.lang("Do not write the following fields to XMP Metadata:")); - private final TableView tableView = new TableView<>(); - - /** - * Customization of external program paths. - */ - public XmpPrefsTab(JabRefPreferences prefs) { - this.prefs = Objects.requireNonNull(prefs); - - tableView.itemsProperty().bindBidirectional(fields); - TableColumn column = new TableColumn<>(); - column.setCellValueFactory(cellData -> cellData.getValue().field()); - - TableColumn deleteIconColumn = new TableColumn<>(); - deleteIconColumn.setPrefWidth(60); - deleteIconColumn.setCellValueFactory(cellData -> cellData.getValue().field()); - new ValueTableCellFactory() - .withGraphic(item -> JabRefIcons.DELETE_ENTRY.getGraphicNode()) - .withOnMouseClickedEvent(item -> evt -> delete()) - .install(deleteIconColumn); - - column.setOnEditCommit((CellEditEvent cell) -> { - cell.getRowValue().setField(cell.getNewValue()); - }); - - tableView.getColumns().setAll(column, deleteIconColumn); - tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - - ComboBox bibtexFields = new ComboBox<>(FXCollections.observableArrayList(FieldFactory.getCommonFields().stream().map(Field::getName).collect(Collectors.toSet()))); - bibtexFields.setEditable(true); - - BorderPane tablePanel = new BorderPane(); - ScrollPane scrollPane = new ScrollPane(); - scrollPane.setMaxHeight(400); - scrollPane.setMaxWidth(400); - scrollPane.setContent(tableView); - tablePanel.setCenter(scrollPane); - - Button add = new Button("Add"); - add.setGraphic(JabRefIcons.ADD.getGraphicNode()); - add.setOnAction(e -> { - if (!StringUtil.isNullOrEmpty(bibtexFields.getSelectionModel().getSelectedItem())) { - XMPPrivacyFilter tableRow = new XMPPrivacyFilter(bibtexFields.getSelectionModel().getSelectedItem()); - fields.add(tableRow); - } - }); - - HBox toolbar = new HBox(bibtexFields, add); - tablePanel.setBottom(toolbar); - - // Build Prefs Tabs - Label xmpExportPrivacySettings = new Label(Localization.lang("XMP export privacy settings")); - xmpExportPrivacySettings.getStyleClass().add("sectionHeader"); - builder.add(xmpExportPrivacySettings, 1, 1); - builder.add(privacyFilterCheckBox, 1, 2); - builder.add(tablePanel, 1, 3); - - tableView.disableProperty().bind(privacyFilterCheckBox.selectedProperty().not()); - add.disableProperty().bind(privacyFilterCheckBox.selectedProperty().not()); - } - - private void delete() { - if (tableView.getSelectionModel().getSelectedItem() != null) { - XMPPrivacyFilter tableRow = tableView.getSelectionModel().getSelectedItem(); - fields.remove(tableRow); - } - } - - @Override - public Node getBuilder() { - return builder; - } - - /** - * Load settings from the preferences and initialize the table. - */ - @Override - public void setValues() { - List xmpExclusions = prefs.getStringList(JabRefPreferences.XMP_PRIVACY_FILTERS).stream().map(XMPPrivacyFilter::new).collect(Collectors.toList()); - fields.setAll(xmpExclusions); - privacyFilterCheckBox.setSelected(JabRefPreferences.getInstance().getBoolean(JabRefPreferences.USE_XMP_PRIVACY_FILTER)); - } - - /** - * Store changes to table preferences. This method is called when the user - * clicks Ok. - * - */ - @Override - public void storeSettings() { - - fields.stream().filter(s -> StringUtil.isNullOrEmpty(s.getField())).forEach(fields::remove); - prefs.putStringList(JabRefPreferences.XMP_PRIVACY_FILTERS, - fields.stream().map(XMPPrivacyFilter::getField).collect(Collectors.toList())); - prefs.putBoolean(JabRefPreferences.USE_XMP_PRIVACY_FILTER, privacyFilterCheckBox.isSelected()); - } - - @Override - public boolean validateSettings() { - return true; - } - - @Override - public String getTabName() { - return Localization.lang("XMP-metadata"); - } - - @Override - public List getRestartWarnings() { return new ArrayList<>(); } - - private class XMPPrivacyFilter { - - private final SimpleStringProperty field; - - XMPPrivacyFilter(String field) { - this.field = new SimpleStringProperty(field); - } - - public void setField(String field) { - this.field.set(field); - } - - public String getField() { - return field.get(); - } - - public StringProperty field() { - return field; - } - - @Override - public String toString() { - return field.getValue(); - } - } -} diff --git a/src/main/java/org/jabref/gui/preferences/XmpPrivacyTab.fxml b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTab.fxml new file mode 100644 index 00000000000..d9684450cc5 --- /dev/null +++ b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTab.fxml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabView.java b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabView.java new file mode 100644 index 00000000000..a977be8ccf3 --- /dev/null +++ b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabView.java @@ -0,0 +1,111 @@ +package org.jabref.gui.preferences; + +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ComboBox; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.input.KeyCode; +import javafx.util.StringConverter; + +import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.util.BindingsHelper; +import org.jabref.gui.util.FieldsUtil; +import org.jabref.gui.util.IconValidationDecorator; +import org.jabref.gui.util.ValueTableCellFactory; +import org.jabref.gui.util.ViewModelListCellFactory; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.FieldFactory; +import org.jabref.preferences.JabRefPreferences; + +import com.airhacks.afterburner.views.ViewLoader; +import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer; + +public class XmpPrivacyTabView extends AbstractPreferenceTabView implements PreferencesTab { + + @FXML private CheckBox enableXmpFilter; + @FXML private TableView filterList; + @FXML private TableColumn fieldColumn; + @FXML private TableColumn actionsColumn; + @FXML private ComboBox addFieldName; + @FXML private Button addField; + + private ControlsFxVisualizer validationVisualizer = new ControlsFxVisualizer(); + + public XmpPrivacyTabView(JabRefPreferences preferences) { + this.preferences = preferences; + + ViewLoader.view(this) + .root(this) + .load(); + } + + @Override + public String getTabName() { return Localization.lang("XMP-metadata"); } + + public void initialize () { + XmpPrivacyTabViewModel xmpPrivacyTabViewModel = new XmpPrivacyTabViewModel(dialogService, preferences); + this.viewModel = xmpPrivacyTabViewModel; + + enableXmpFilter.selectedProperty().bindBidirectional(xmpPrivacyTabViewModel.xmpFilterEnabledProperty()); + filterList.disableProperty().bind(xmpPrivacyTabViewModel.xmpFilterEnabledProperty().not()); + addFieldName.disableProperty().bind(xmpPrivacyTabViewModel.xmpFilterEnabledProperty().not()); + addField.disableProperty().bind(xmpPrivacyTabViewModel.xmpFilterEnabledProperty().not()); + + fieldColumn.setSortable(true); + fieldColumn.setReorderable(false); + fieldColumn.setCellValueFactory(cellData -> BindingsHelper.constantOf(cellData.getValue())); + new ValueTableCellFactory() + .withText(FieldsUtil::getNameWithType) + .install(fieldColumn); + + actionsColumn.setSortable(false); + actionsColumn.setReorderable(false); + actionsColumn.setCellValueFactory(cellData -> BindingsHelper.constantOf(cellData.getValue())); + new ValueTableCellFactory() + .withGraphic(item -> IconTheme.JabRefIcons.DELETE_ENTRY.getGraphicNode()) + .withTooltip(item -> Localization.lang("Remove") + " " + item.getName()) + .withOnMouseClickedEvent(item -> evt -> { + xmpPrivacyTabViewModel.removeFilter(filterList.getFocusModel().getFocusedItem()); + }) + .install(actionsColumn); + + filterList.setOnKeyPressed(event -> { + if (event.getCode() == KeyCode.DELETE) { + xmpPrivacyTabViewModel.removeFilter(filterList.getSelectionModel().getSelectedItem()); + } + }); + + filterList.itemsProperty().bind(xmpPrivacyTabViewModel.filterListProperty()); + + addFieldName.setEditable(true); + new ViewModelListCellFactory() + .withText(FieldsUtil::getNameWithType) + .install(addFieldName); + addFieldName.itemsProperty().bind(xmpPrivacyTabViewModel.availableFieldsProperty()); + addFieldName.valueProperty().bindBidirectional(xmpPrivacyTabViewModel.addFieldNameProperty()); + addFieldName.setConverter(new StringConverter<>() { + @Override + public String toString(Field object) { + if (object != null) { + return object.getDisplayName(); + } else { + return ""; + } + } + + @Override + public Field fromString(String string) { + return FieldFactory.parseField(string); + } + }); + + validationVisualizer.setDecoration(new IconValidationDecorator()); + Platform.runLater(() -> validationVisualizer.initVisualization(xmpPrivacyTabViewModel.xmpFilterListValidationStatus(), filterList)); + } + + public void addField() { ((XmpPrivacyTabViewModel) viewModel).addField(); } +} diff --git a/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabViewModel.java b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabViewModel.java new file mode 100644 index 00000000000..ad9d5b7445f --- /dev/null +++ b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabViewModel.java @@ -0,0 +1,114 @@ +package org.jabref.gui.preferences; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ListProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; + +import org.jabref.gui.DialogService; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.FieldFactory; +import org.jabref.preferences.JabRefPreferences; + +import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator; +import de.saxsys.mvvmfx.utils.validation.ValidationMessage; +import de.saxsys.mvvmfx.utils.validation.ValidationStatus; + +public class XmpPrivacyTabViewModel implements PreferenceTabViewModel { + + private final BooleanProperty xmpFilterEnabledProperty = new SimpleBooleanProperty(); + private final ListProperty xmpFilterListProperty = new SimpleListProperty<>(FXCollections.observableArrayList()); + private final ListProperty availableFieldsProperty = new SimpleListProperty<>(FXCollections.observableArrayList()); + private final ObjectProperty addFieldProperty = new SimpleObjectProperty<>(); + + private final DialogService dialogService; + private final JabRefPreferences preferences; + + private FunctionBasedValidator xmpFilterListValidator; + + XmpPrivacyTabViewModel(DialogService dialogService, JabRefPreferences preferences) { + this.dialogService = dialogService; + this.preferences = preferences; + + xmpFilterListValidator = new FunctionBasedValidator<>( + xmpFilterListProperty, + input -> input.size() > 0, + ValidationMessage.error(String.format("%s > %s %n %n %s", + Localization.lang("xmp-metadata"), + Localization.lang("Filter List"), + Localization.lang("List must not be empty.")))); + + setValues(); + } + + @Override + public void setValues() { + xmpFilterEnabledProperty.setValue(preferences.getBoolean(JabRefPreferences.USE_XMP_PRIVACY_FILTER)); + + xmpFilterListProperty.clear(); + List xmpFilters = preferences.getStringList(JabRefPreferences.XMP_PRIVACY_FILTERS) + .stream().map(FieldFactory::parseField).collect(Collectors.toList()); + xmpFilterListProperty.addAll(xmpFilters); + + availableFieldsProperty.clear(); + availableFieldsProperty.addAll(FieldFactory.getCommonFields()); + } + + @Override + public void storeSettings() { + preferences.putBoolean(JabRefPreferences.USE_XMP_PRIVACY_FILTER, xmpFilterEnabledProperty.getValue()); + preferences.putStringList(JabRefPreferences.XMP_PRIVACY_FILTERS, xmpFilterListProperty.getValue().stream() + .map(Field::getName) + .collect(Collectors.toList())); + } + + public void addField() { + if (addFieldProperty.getValue() == null) { + return; + } + + if (xmpFilterListProperty.getValue().stream().filter(item -> item.equals(addFieldProperty.getValue())).findAny().isEmpty()) { + xmpFilterListProperty.add(addFieldProperty.getValue()); + addFieldProperty.setValue(null); + } + } + + public void removeFilter(Field filter) { + xmpFilterListProperty.remove(filter); + } + + public ValidationStatus xmpFilterListValidationStatus() { + return xmpFilterListValidator.getValidationStatus(); + } + + @Override + public boolean validateSettings() { + ValidationStatus validationStatus = xmpFilterListValidationStatus(); + if (xmpFilterEnabledProperty.getValue() && !validationStatus.isValid()) { + validationStatus.getHighestMessage().ifPresent(message -> + dialogService.showErrorDialogAndWait(message.getMessage())); + return false; + } + return true; + } + + @Override + public List getRestartWarnings() { return new ArrayList<>(); } + + public BooleanProperty xmpFilterEnabledProperty() { return xmpFilterEnabledProperty; } + + public ListProperty filterListProperty() { return xmpFilterListProperty; } + + public ListProperty availableFieldsProperty() { return availableFieldsProperty; } + + public ObjectProperty addFieldNameProperty() { return addFieldProperty; } + +} diff --git a/src/main/java/org/jabref/gui/util/FieldsUtil.java b/src/main/java/org/jabref/gui/util/FieldsUtil.java new file mode 100644 index 00000000000..e313a064481 --- /dev/null +++ b/src/main/java/org/jabref/gui/util/FieldsUtil.java @@ -0,0 +1,55 @@ +package org.jabref.gui.util; + +import java.util.Collections; +import java.util.Set; + +import org.jabref.logic.l10n.Localization; +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.FieldProperty; +import org.jabref.model.entry.field.IEEEField; +import org.jabref.model.entry.field.InternalField; +import org.jabref.model.entry.field.SpecialField; +import org.jabref.model.entry.field.UnknownField; + +public class FieldsUtil { + + public static String getNameWithType(Field field) { + if (field instanceof SpecialField) { + return field.getDisplayName() + " (" + Localization.lang("Special") + ")"; + } else if (field instanceof IEEEField) { + return field.getDisplayName() + " (" + Localization.lang("IEEE") + ")"; + } else if (field instanceof InternalField) { + return field.getDisplayName() + " (" + Localization.lang("Internal") + ")"; + } else if (field instanceof UnknownField) { + return field.getDisplayName() + " (" + Localization.lang("Custom") + ")"; + } else if (field instanceof ExtraFilePseudoField) { + return field.getDisplayName() + " (" + Localization.lang("File type") + ")"; + } else { + return field.getDisplayName(); + } + } + + public static class ExtraFilePseudoField implements Field { + + String name; + + public ExtraFilePseudoField(String name) { + this.name = name; + } + + @Override + public Set getProperties() { + return Collections.emptySet(); + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isStandardField() { + return false; + } + } +} diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index d33984a30b1..639970999d2 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -234,8 +234,6 @@ delete\ entry=delete entry Delete\ multiple\ entries=Delete multiple entries -Delete\ rows=Delete rows - Deleted=Deleted Permanently\ delete\ local\ file=Permanently delete local file @@ -264,7 +262,7 @@ Do\ not\ open\ any\ files\ at\ startup=Do not open any files at startup Do\ not\ overwrite\ existing\ keys=Do not overwrite existing keys Do\ not\ wrap\ the\ following\ fields\ when\ saving=Do not wrap the following fields when saving -Do\ not\ write\ the\ following\ fields\ to\ XMP\ Metadata\:=Do not write the following fields to XMP Metadata: +Do\ not\ write\ the\ following\ fields\ to\ XMP\ Metadata=Do not write the following fields to XMP Metadata Donate\ to\ JabRef=Donate to JabRef @@ -483,7 +481,6 @@ Include\ subgroups\:\ When\ selected,\ view\ entries\ contained\ in\ this\ group Independent\ group\:\ When\ selected,\ view\ only\ this\ group's\ entries=Independent group: When selected, view only this group's entries I\ Agree=I Agree -Insert\ rows=Insert rows Invalid\ BibTeX\ key=Invalid BibTeX key @@ -563,6 +560,7 @@ No\ recommendations\ received\ from\ Mr.\ DLib\ for\ this\ entry.=No recommendat Error\ while\ fetching\ recommendations\ from\ Mr.DLib.=Error while fetching recommendations from Mr.DLib. Name=Name + Name\ formatter=Name formatter Natbib\ style=Natbib style @@ -2114,3 +2112,13 @@ Set\ LaTeX\ file\ directory=Set LaTeX file directory Import\ entries\ from\ LaTeX\ files=Import entries from LaTeX files Import\ new\ entries=Import new entries Group\ color=Group color + +Add\ field\ to\ filter\ list=Add field to filter list +Add\ formatter\ to\ list=Add formatter to list +IEEE=IEEE +Internal=Internal +Special=Special +File\ type=File type +Filter\ List=Filter List +List\ must\ not\ be\ empty.=List must not be empty. +xmp-metadata=xmp-metadata From 99a0a7a8dd79dddc18d5157538b1c66ac9fd0065 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Thu, 5 Sep 2019 08:28:52 +0200 Subject: [PATCH 050/560] Revert "[WIP] Get JabRef running in eclipse" (#5280) --- .gitmodules | 3 +++ eclipse.gradle | 32 ++++-------------------- src/main/java/org/jabref/JabRefMain.java | 2 +- src/main/resources/csl-locales | 2 +- src/main/resources/csl-styles | 1 + 5 files changed, 11 insertions(+), 29 deletions(-) create mode 160000 src/main/resources/csl-styles diff --git a/.gitmodules b/.gitmodules index 28a38913ff8..b37b24cf6d9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ +[submodule "src/main/resources/csl-styles"] + path = src/main/resources/csl-styles + url = https://github.com/citation-style-language/styles.git [submodule "src/main/resources/csl-locales"] path = src/main/resources/csl-locales url = https://github.com/citation-style-language/locales.git diff --git a/eclipse.gradle b/eclipse.gradle index 8f3aaa17d00..6fa381b3fe7 100644 --- a/eclipse.gradle +++ b/eclipse.gradle @@ -11,39 +11,17 @@ eclipseJdt.doLast { f.append('encoding/=UTF-8') } eclipse { - project { - natures 'org.eclipse.buildship.core.gradleprojectnature' - } - - classpath { +//add libraries to module path: https://github.com/eclipse/buildship/issues/620#issuecomment-390469550 + classpath { file { whenMerged { - entries.findAll { isModule(it) }.each { //(1) - it.entryAttributes['module'] = 'true' - } - - entries.findAll { isSource(it) && isTestScope(it) }.each { - it.entryAttributes['test'] = 'true' - } - - entries.findAll { isLibrary(it) && isTestScope(it) }.each { - it.entryAttributes['test'] = 'true' - } + entries.findAll { + it.kind == 'src' || it.kind == 'lib' + }.each { it.entryAttributes['module'] = 'true' } } } - - defaultOutputDir = file('build') - downloadSources = true - downloadJavadoc = true } } - -boolean isLibrary(entry) { return entry.properties.kind.equals('lib') } -boolean isTestScope(entry) { return entry.entryAttributes.get('gradle_used_by_scope').equals('test'); } -boolean isModule(entry) { isLibrary(entry) && !isTestScope(entry); } -boolean isSource(entry) { return entry.properties.kind.equals('src'); } - - // add formatter and cleanup settings to Eclipse settings // see http://stackoverflow.com/a/27461890/873282 diff --git a/src/main/java/org/jabref/JabRefMain.java b/src/main/java/org/jabref/JabRefMain.java index 93a16422b46..72051540247 100644 --- a/src/main/java/org/jabref/JabRefMain.java +++ b/src/main/java/org/jabref/JabRefMain.java @@ -48,7 +48,7 @@ public static void main(String[] args) { public void start(Stage mainStage) throws Exception { try { // Fail on unsupported Java versions - // ensureCorrectJavaVersion(); + ensureCorrectJavaVersion(); FallbackExceptionHandler.installExceptionHandler(); // Init preferences diff --git a/src/main/resources/csl-locales b/src/main/resources/csl-locales index 29ed2ff4328..e89e6b08b5c 160000 --- a/src/main/resources/csl-locales +++ b/src/main/resources/csl-locales @@ -1 +1 @@ -Subproject commit 29ed2ff43284f726f9f583981650a86ffb9b236f +Subproject commit e89e6b08b5c621a414fc7114f2129efac5f8c7d5 diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles new file mode 160000 index 00000000000..fa125ec03c6 --- /dev/null +++ b/src/main/resources/csl-styles @@ -0,0 +1 @@ +Subproject commit fa125ec03c6270dea5881ec1623f1b741da6e759 From d183c605ac9ef3e32a4c96b779a5665f2525a4cc Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Thu, 5 Sep 2019 09:35:06 +0200 Subject: [PATCH 051/560] Make JabRef work in eclipse This is the only change (plus the manual configuration) --- eclipse.gradle | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/eclipse.gradle b/eclipse.gradle index 6fa381b3fe7..eeaa9f4895f 100644 --- a/eclipse.gradle +++ b/eclipse.gradle @@ -11,17 +11,39 @@ eclipseJdt.doLast { f.append('encoding/=UTF-8') } eclipse { -//add libraries to module path: https://github.com/eclipse/buildship/issues/620#issuecomment-390469550 - classpath { + project { + natures 'org.eclipse.buildship.core.gradleprojectnature' + } + + classpath { file { whenMerged { - entries.findAll { - it.kind == 'src' || it.kind == 'lib' - }.each { it.entryAttributes['module'] = 'true' } + entries.findAll { isModule(it) }.each { //(1) + it.entryAttributes['module'] = 'true' + } + + entries.findAll { isSource(it) && isTestScope(it) }.each { + it.entryAttributes['test'] = 'true' + } + + entries.findAll { isLibrary(it) && isTestScope(it) }.each { + it.entryAttributes['test'] = 'true' + } } } + + defaultOutputDir = file('build') + downloadSources = true + downloadJavadoc = true } } + +boolean isLibrary(entry) { return entry.properties.kind.equals('lib') } +boolean isTestScope(entry) { return entry.entryAttributes.get('gradle_used_by_scope').equals('test'); } +boolean isModule(entry) { isLibrary(entry) && !isTestScope(entry); } +boolean isSource(entry) { return entry.properties.kind.equals('src'); } + + // add formatter and cleanup settings to Eclipse settings // see http://stackoverflow.com/a/27461890/873282 @@ -577,4 +599,4 @@ tasks.eclipse.doFirst { sp_cleanup.use_type_arguments=false '''. stripIndent()) } -} +} \ No newline at end of file From 535a0addb627b0bd1dc344f394036e2502e37c2d Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Sat, 7 Sep 2019 17:40:56 +0200 Subject: [PATCH 052/560] [WIP] Try to fix LibreOffice class loader The problem is that it somehow doesn't load the jars from the LO path, but from our custom module --- .../org/jabref/gui/openoffice/OOBibBase.java | 31 ++++++------------- .../gui/openoffice/OpenOfficePanel.java | 27 ++++++---------- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/jabref/gui/openoffice/OOBibBase.java b/src/main/java/org/jabref/gui/openoffice/OOBibBase.java index ce72f55294e..7814a52d4e3 100644 --- a/src/main/java/org/jabref/gui/openoffice/OOBibBase.java +++ b/src/main/java/org/jabref/gui/openoffice/OOBibBase.java @@ -7,6 +7,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -131,7 +132,7 @@ class OOBibBase { private final DialogService dialogService; - public OOBibBase(String pathToOO, boolean atEnd, DialogService dialogService) throws IllegalAccessException, InvocationTargetException, BootstrapException, CreationException, IOException { + public OOBibBase(String pathToOO, boolean atEnd, DialogService dialogService) throws IllegalAccessException, InvocationTargetException, BootstrapException, CreationException, IOException, ClassNotFoundException { this.dialogService = dialogService; @@ -147,7 +148,9 @@ public OOBibBase(String pathToOO, boolean atEnd, DialogService dialogService) th yearAuthorTitleComparator = new FieldComparatorStack<>(yearAuthorTitleList); this.atEnd = atEnd; - xDesktop = simpleBootstrap(pathToOO); + + xDesktop = simpleBootstrap(pathToOO); + } @@ -237,26 +240,12 @@ private List getTextDocuments() throws NoSuchElementException, Wr private XDesktop simpleBootstrap(String pathToExecutable) throws IllegalAccessException, InvocationTargetException, BootstrapException, - CreationException, IOException { - - ClassLoader loader = ClassLoader.getSystemClassLoader(); - if (loader instanceof URLClassLoader) { - URLClassLoader cl = (URLClassLoader) loader; - Class sysclass = URLClassLoader.class; - try { - Method method = sysclass.getDeclaredMethod("addURL", URL.class); - method.setAccessible(true); - method.invoke(cl, new File(pathToExecutable).toURI().toURL()); - } catch (SecurityException | NoSuchMethodException | MalformedURLException t) { - LOGGER.error("Error, could not add URL to system classloader", t); - cl.close(); - throw new IOException("Error, could not add URL to system classloader", t); - } - } else { - LOGGER.error("Error occured, URLClassLoader expected but " + loader.getClass() - + " received. Could not continue."); - } + CreationException, IOException, ClassNotFoundException { + + URL[] urls = { Paths.get(pathToExecutable).toUri().toURL()}; + Class clazz = Class.forName("com.sun.star.comp.helper.Bootstrap", true, new URLClassLoader(urls)); + //Get the office component context: XComponentContext xContext = Bootstrap.bootstrap(); //Get the office service manager: diff --git a/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java b/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java index c56e7606c29..188122d298d 100644 --- a/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java +++ b/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java @@ -137,21 +137,8 @@ public OpenOfficePanel(JabRefFrame frame, JabRefPreferences jabRefPreferences, O initPanel(); } - private static void addURL(List jarList) throws IOException { - URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader(); - Class sysclass = URLClassLoader.class; - try { - Method method = sysclass.getDeclaredMethod("addURL", URL.class); - method.setAccessible(true); - for (URL anU : jarList) { - method.invoke(sysloader, anU); - } - } catch (SecurityException | NoSuchMethodException | IllegalAccessException | IllegalArgumentException | - InvocationTargetException e) { - LOGGER.error("Could not add URL to system classloader", e); - sysloader.close(); - throw new IOException("Error, could not add URL to system classloader", e); - } + private static void addURL(List jarList) throws IOException, ClassNotFoundException { + Class clazz = Class.forName("com.sun.star.comp.helper.Bootstrap", true, new URLClassLoader( jarList.toArray(new URL[0]) )); } public Node getContent() { @@ -453,12 +440,18 @@ private void loadOpenOfficeJars(Path configurationPath) throws IOException { for (Optional jarPath : filePaths) { jarURLs.add((jarPath.get().toUri().toURL())); } - addURL(jarURLs); + + try { + addURL(jarURLs); + } catch (ClassNotFoundException e) { + LOGGER.error("lo error",e); + } } private OOBibBase createBibBase() throws IOException, InvocationTargetException, IllegalAccessException, - BootstrapException, CreationException { + BootstrapException, CreationException, ClassNotFoundException { // Connect + return new OOBibBase(ooPrefs.getExecutablePath(), true, dialogService); } From e81457a7561d721a006abe07549032147a267ca8 Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Sat, 7 Sep 2019 18:30:25 +0200 Subject: [PATCH 053/560] refactor and try with custom class loader --- .../org/jabref/gui/openoffice/OOBibBase.java | 19 ++++------- .../gui/openoffice/OpenOfficePanel.java | 23 +++++-------- .../openoffice/ChildFirstClassLoader.java | 33 +++++++++++++++++++ 3 files changed, 49 insertions(+), 26 deletions(-) create mode 100644 src/main/java/org/jabref/logic/openoffice/ChildFirstClassLoader.java diff --git a/src/main/java/org/jabref/gui/openoffice/OOBibBase.java b/src/main/java/org/jabref/gui/openoffice/OOBibBase.java index 7814a52d4e3..a4fa5db2ff7 100644 --- a/src/main/java/org/jabref/gui/openoffice/OOBibBase.java +++ b/src/main/java/org/jabref/gui/openoffice/OOBibBase.java @@ -1,12 +1,8 @@ package org.jabref.gui.openoffice; -import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.MalformedURLException; import java.net.URL; -import java.net.URLClassLoader; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -32,6 +28,7 @@ import org.jabref.logic.bibtex.comparator.FieldComparatorStack; import org.jabref.logic.l10n.Localization; import org.jabref.logic.layout.Layout; +import org.jabref.logic.openoffice.ChildFirstClassLoader; import org.jabref.logic.openoffice.OOBibStyle; import org.jabref.logic.openoffice.OOPreFormatter; import org.jabref.logic.openoffice.OOUtil; @@ -132,7 +129,7 @@ class OOBibBase { private final DialogService dialogService; - public OOBibBase(String pathToOO, boolean atEnd, DialogService dialogService) throws IllegalAccessException, InvocationTargetException, BootstrapException, CreationException, IOException, ClassNotFoundException { + public OOBibBase(List jarUrls, boolean atEnd, DialogService dialogService) throws IllegalAccessException, InvocationTargetException, BootstrapException, CreationException, IOException, ClassNotFoundException { this.dialogService = dialogService; @@ -149,9 +146,7 @@ public OOBibBase(String pathToOO, boolean atEnd, DialogService dialogService) th this.atEnd = atEnd; - xDesktop = simpleBootstrap(pathToOO); - - + xDesktop = simpleBootstrap(jarUrls); } public boolean isConnectedToDocument() { @@ -238,14 +233,14 @@ private List getTextDocuments() throws NoSuchElementException, Wr return result; } - private XDesktop simpleBootstrap(String pathToExecutable) + private XDesktop simpleBootstrap(List jarUrls) throws IllegalAccessException, InvocationTargetException, BootstrapException, CreationException, IOException, ClassNotFoundException { - URL[] urls = { Paths.get(pathToExecutable).toUri().toURL()}; - Class clazz = Class.forName("com.sun.star.comp.helper.Bootstrap", true, new URLClassLoader(urls)); - + URL[] urls = jarUrls.toArray(new URL[3]); + Class clazz = Class.forName("com.sun.star.comp.helper.Bootstrap", true, new ChildFirstClassLoader(urls, null)); + //Get the office component context: XComponentContext xContext = Bootstrap.bootstrap(); //Get the office service manager: diff --git a/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java b/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java index 188122d298d..2bec311bc4e 100644 --- a/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java +++ b/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java @@ -3,9 +3,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.net.URL; -import java.net.URLClassLoader; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -45,6 +43,7 @@ import org.jabref.logic.bibtexkeypattern.BibtexKeyPatternPreferences; import org.jabref.logic.help.HelpFile; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.openoffice.ChildFirstClassLoader; import org.jabref.logic.openoffice.OOBibStyle; import org.jabref.logic.openoffice.OpenOfficePreferences; import org.jabref.logic.openoffice.StyleLoader; @@ -138,7 +137,7 @@ public OpenOfficePanel(JabRefFrame frame, JabRefPreferences jabRefPreferences, O } private static void addURL(List jarList) throws IOException, ClassNotFoundException { - Class clazz = Class.forName("com.sun.star.comp.helper.Bootstrap", true, new URLClassLoader( jarList.toArray(new URL[0]) )); + // Class clazz = Class.forName("com.sun.star.comp.helper.Bootstrap", true, new ChildFirstClassLoader( jarList.toArray(new URL[0]),null )); } public Node getContent() { @@ -373,9 +372,9 @@ private void connect() { @Override protected OOBibBase call() throws Exception { updateProgress(ProgressBar.INDETERMINATE_PROGRESS, ProgressBar.INDETERMINATE_PROGRESS); - loadOpenOfficeJars(Paths.get(ooPrefs.getInstallationPath())); + List jarUrls = loadOpenOfficeJars(Paths.get(ooPrefs.getInstallationPath())); - return createBibBase(); + return createBibBase(jarUrls); } }; @@ -429,7 +428,7 @@ protected OOBibBase call() throws Exception { } - private void loadOpenOfficeJars(Path configurationPath) throws IOException { + private List loadOpenOfficeJars(Path configurationPath) throws IOException { List> filePaths = OpenOfficePreferences.OO_JARS.stream().map(jar -> FileUtil.find(jar, configurationPath)).collect(Collectors.toList()); if (!filePaths.stream().allMatch(Optional::isPresent)) { @@ -440,19 +439,15 @@ private void loadOpenOfficeJars(Path configurationPath) throws IOException { for (Optional jarPath : filePaths) { jarURLs.add((jarPath.get().toUri().toURL())); } - - try { - addURL(jarURLs); - } catch (ClassNotFoundException e) { - LOGGER.error("lo error",e); - } + return jarURLs; + } - private OOBibBase createBibBase() throws IOException, InvocationTargetException, IllegalAccessException, + private OOBibBase createBibBase(List jarUrls) throws IOException, InvocationTargetException, IllegalAccessException, BootstrapException, CreationException, ClassNotFoundException { // Connect - return new OOBibBase(ooPrefs.getExecutablePath(), true, dialogService); + return new OOBibBase(jarUrls,true, dialogService); } private Optional showManualConnectionDialog() { diff --git a/src/main/java/org/jabref/logic/openoffice/ChildFirstClassLoader.java b/src/main/java/org/jabref/logic/openoffice/ChildFirstClassLoader.java new file mode 100644 index 00000000000..a09a53688f2 --- /dev/null +++ b/src/main/java/org/jabref/logic/openoffice/ChildFirstClassLoader.java @@ -0,0 +1,33 @@ +package org.jabref.logic.openoffice; + +import java.net.URL; +import java.net.URLClassLoader; + +public class ChildFirstClassLoader extends URLClassLoader { + + public ChildFirstClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + // has the class loaded already? + Class loadedClass = findLoadedClass(name); + if (loadedClass == null) { + try { + // find the class from given jar urls + loadedClass = findClass(name); + } catch (ClassNotFoundException e) { + // Hmmm... class does not exist in the given urls. + // Let's try finding it in our parent classloader. + // this'll throw ClassNotFoundException in failure. + loadedClass = super.loadClass(name, resolve); + } + } + + if (resolve) { // marked to resolve + resolveClass(loadedClass); + } + return loadedClass; + } +} \ No newline at end of file From 164843bd45eb6fec25367a6246e661862ed38c5e Mon Sep 17 00:00:00 2001 From: Dawid Owoc <47257983+dawidowoc@users.noreply.github.com> Date: Sun, 8 Sep 2019 10:39:49 +0200 Subject: [PATCH 054/560] Add shorten DOI field formatter (koppor#343) (#5276) --- CHANGELOG.md | 2 + .../jabref/logic/formatter/Formatters.java | 5 +- .../bibtexfields/ShortenDOIFormatter.java | 59 ++++++++++++ .../logic/importer/util/ShortDOIService.java | 63 ++++++++++++ .../util/ShortDOIServiceException.java | 25 +++++ .../jabref/model/entry/identifier/DOI.java | 95 ++++++++++++++----- src/main/resources/l10n/JabRef_en.properties | 2 + .../bibtexfields/ShortenDOIFormatterTest.java | 24 +++++ .../importer/util/ShortDOIServiceTest.java | 36 +++++++ .../model/entry/identifier/DOITest.java | 86 +++++++++++++++++ 10 files changed, 372 insertions(+), 25 deletions(-) create mode 100644 src/main/java/org/jabref/logic/formatter/bibtexfields/ShortenDOIFormatter.java create mode 100644 src/main/java/org/jabref/logic/importer/util/ShortDOIService.java create mode 100644 src/main/java/org/jabref/logic/importer/util/ShortDOIServiceException.java create mode 100644 src/test/java/org/jabref/logic/formatter/bibtexfields/ShortenDOIFormatterTest.java create mode 100644 src/test/java/org/jabref/logic/importer/util/ShortDOIServiceTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c9853c1f24..725fe30477c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# ### Changed +- We added a short DOI field formatter which shortens DOI to more human readable form. [koppor#343](https://github.com/koppor/jabref/issues/343) + ### Fixed ### Removed diff --git a/src/main/java/org/jabref/logic/formatter/Formatters.java b/src/main/java/org/jabref/logic/formatter/Formatters.java index 9e867ac23ed..7aea0a024a6 100644 --- a/src/main/java/org/jabref/logic/formatter/Formatters.java +++ b/src/main/java/org/jabref/logic/formatter/Formatters.java @@ -19,6 +19,7 @@ import org.jabref.logic.formatter.bibtexfields.OrdinalsToSuperscriptFormatter; import org.jabref.logic.formatter.bibtexfields.RegexFormatter; import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; +import org.jabref.logic.formatter.bibtexfields.ShortenDOIFormatter; import org.jabref.logic.formatter.bibtexfields.UnicodeToLatexFormatter; import org.jabref.logic.formatter.bibtexfields.UnitsToLatexFormatter; import org.jabref.logic.formatter.casechanger.CapitalizeFormatter; @@ -67,7 +68,8 @@ public static List getOthers() { new OrdinalsToSuperscriptFormatter(), new RemoveBracesFormatter(), new UnitsToLatexFormatter(), - new EscapeUnderscoresFormatter() + new EscapeUnderscoresFormatter(), + new ShortenDOIFormatter() ); } @@ -102,5 +104,4 @@ public static Optional getFormatterForModifier(String modifier) { return getAll().stream().filter(f -> f.getKey().equals(modifier)).findAny(); } } - } diff --git a/src/main/java/org/jabref/logic/formatter/bibtexfields/ShortenDOIFormatter.java b/src/main/java/org/jabref/logic/formatter/bibtexfields/ShortenDOIFormatter.java new file mode 100644 index 00000000000..75b9ba9a2ab --- /dev/null +++ b/src/main/java/org/jabref/logic/formatter/bibtexfields/ShortenDOIFormatter.java @@ -0,0 +1,59 @@ +package org.jabref.logic.formatter.bibtexfields; + +import java.util.Objects; +import java.util.Optional; + +import org.jabref.logic.importer.util.ShortDOIService; +import org.jabref.logic.importer.util.ShortDOIServiceException; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.cleanup.Formatter; +import org.jabref.model.entry.identifier.DOI; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ShortenDOIFormatter extends Formatter { + + private static final Logger LOGGER = LoggerFactory.getLogger(ShortenDOIFormatter.class); + + @Override + public String getName() { + return Localization.lang("Shorten DOI"); + } + + @Override + public String getKey() { + return "short_doi"; + } + + @Override + public String format(String value) { + Objects.requireNonNull(value); + + ShortDOIService shortDOIService = new ShortDOIService(); + + Optional doi = Optional.empty(); + + try { + doi = DOI.parse(value); + + if (doi.isPresent()) { + return shortDOIService.getShortDOI(doi.get()).getDOI(); + } + } catch (ShortDOIServiceException e) { + LOGGER.error(e.getMessage(), e); + } + + return value; + } + + @Override + public String getDescription() { + return Localization.lang("Shortens DOI to more human readable form."); + } + + @Override + public String getExampleInput() { + return "10.1006/jmbi.1998.2354"; + } +} diff --git a/src/main/java/org/jabref/logic/importer/util/ShortDOIService.java b/src/main/java/org/jabref/logic/importer/util/ShortDOIService.java new file mode 100644 index 00000000000..bbb3dc7b4c6 --- /dev/null +++ b/src/main/java/org/jabref/logic/importer/util/ShortDOIService.java @@ -0,0 +1,63 @@ +package org.jabref.logic.importer.util; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import org.jabref.logic.importer.ParseException; +import org.jabref.logic.net.URLDownload; +import org.jabref.model.entry.identifier.DOI; + +import org.apache.http.client.utils.URIBuilder; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Class for obtaining shortened DOI names. + * + * @see http://shortdoi.org + */ +public class ShortDOIService { + + private static final String BASIC_URL = "http://shortdoi.org/"; + + /** + * Obtains shortened DOI name for given DOI + * + * @param doi DOI + * @return A shortened DOI name + */ + public DOI getShortDOI(DOI doi) throws ShortDOIServiceException { + JSONObject responseJSON = makeRequest(doi); + String shortDoi = responseJSON.getString("ShortDOI"); + + return new DOI(shortDoi); + } + + private JSONObject makeRequest(DOI doi) throws ShortDOIServiceException { + + URIBuilder uriBuilder = null; + URL url = null; + + try { + uriBuilder = new URIBuilder(BASIC_URL); + uriBuilder.setPath(uriBuilder.getPath() + doi.getDOI()); + uriBuilder.addParameter("format", "json"); + + URI uri = uriBuilder.build(); + url = uri.toURL(); + } catch (URISyntaxException | MalformedURLException e) { + throw new ShortDOIServiceException("Cannot get short DOI", e); + } + + URLDownload urlDownload = new URLDownload(url); + + try { + return JsonReader.toJsonObject(urlDownload.asInputStream()); + } catch (ParseException | IOException | JSONException e) { + throw new ShortDOIServiceException("Cannot get short DOI", e); + } + } +} diff --git a/src/main/java/org/jabref/logic/importer/util/ShortDOIServiceException.java b/src/main/java/org/jabref/logic/importer/util/ShortDOIServiceException.java new file mode 100644 index 00000000000..cd7943eba03 --- /dev/null +++ b/src/main/java/org/jabref/logic/importer/util/ShortDOIServiceException.java @@ -0,0 +1,25 @@ +package org.jabref.logic.importer.util; + +import org.jabref.JabRefException; + +public class ShortDOIServiceException extends JabRefException { + public ShortDOIServiceException(String message) { + super(message); + } + + public ShortDOIServiceException(String message, Throwable cause) { + super(message, cause); + } + + public ShortDOIServiceException(String message, String localizedMessage) { + super(message, localizedMessage); + } + + public ShortDOIServiceException(String message, String localizedMessage, Throwable cause) { + super(message, localizedMessage, cause); + } + + public ShortDOIServiceException(Throwable cause) { + super(cause); + } +} 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 310f81e543b..8c02f9a4696 100644 --- a/src/main/java/org/jabref/model/entry/identifier/DOI.java +++ b/src/main/java/org/jabref/model/entry/identifier/DOI.java @@ -14,14 +14,15 @@ import org.slf4j.LoggerFactory; /** - * Class for working with Digital object identifiers (DOIs) + * Class for working with Digital object identifiers (DOIs) and Short DOIs * * @see https://en.wikipedia.org/wiki/Digital_object_identifier + * @see http://shortdoi.org */ public class DOI implements Identifier { private static final Logger LOGGER = LoggerFactory.getLogger(DOI.class); - // DOI resolver + // DOI/Short DOI resolver private static final URI RESOLVER = URI.create("https://doi.org"); // Regex // (see http://www.doi.org/doi_handbook/2_Numbering.html) @@ -43,20 +44,46 @@ public class DOI implements Identifier { + "[/:]" // divider + "(?:[^\\s]+)" // suffix alphanumeric without space + ")"; // end group \1 + + // Regex (Short DOI) + private static final String SHORT_DOI_EXP = "" + + "(?:urn:)?" // optional urn + + "(?:doi:)?" // optional doi + + "(" // begin group \1 + + "10" // directory indicator + + "[/:%]" // divider + + "[a-zA-Z0-9]+" + + ")"; // end group \1 + private static final String FIND_SHORT_DOI_EXP = "" + + "(?:urn:)?" // optional urn + + "(?:doi:)?" // optional doi + + "(" // begin group \1 + + "10" // directory indicator + + "[/:]" // divider + + "[a-zA-Z0-9]+" + + "(?:[^\\s]+)" // suffix alphanumeric without space + + ")"; // end group \1 + private static final String HTTP_EXP = "https?://[^\\s]+?" + DOI_EXP; + private static final String SHORT_DOI_HTTP_EXP = "https?://[^\\s]+?" + SHORT_DOI_EXP; // Pattern private static final Pattern EXACT_DOI_PATT = Pattern.compile("^(?:https?://[^\\s]+?)?" + DOI_EXP + "$", Pattern.CASE_INSENSITIVE); private static final Pattern DOI_PATT = Pattern.compile("(?:https?://[^\\s]+?)?" + FIND_DOI_EXP, Pattern.CASE_INSENSITIVE); + // Pattern (short DOI) + private static final Pattern EXACT_SHORT_DOI_PATT = Pattern.compile("^(?:https?://[^\\s]+?)?" + SHORT_DOI_EXP, Pattern.CASE_INSENSITIVE); + private static final Pattern SHORT_DOI_PATT = Pattern.compile("(?:https?://[^\\s]+?)?" + FIND_SHORT_DOI_EXP, Pattern.CASE_INSENSITIVE); // DOI private final String doi; + // Short DOI + private boolean isShortDoi; /** - * Creates a DOI from various schemes including URL, URN, and plain DOIs. + * Creates a DOI from various schemes including URL, URN, and plain DOIs/Short DOIs. * - * @param doi the DOI string - * @throws NullPointerException if DOI is null - * @throws IllegalArgumentException if doi does not include a valid DOI + * @param doi the DOI/Short DOI string * @return an instance of the DOI class + * @throws NullPointerException if DOI/Short DOI is null + * @throws IllegalArgumentException if doi does not include a valid DOI/Short DOI */ public DOI(String doi) { Objects.requireNonNull(doi); @@ -65,33 +92,40 @@ public DOI(String doi) { String trimmedDoi = doi.trim(); // HTTP URL decoding - if (doi.matches(HTTP_EXP)) { + if (doi.matches(HTTP_EXP) || doi.matches(SHORT_DOI_HTTP_EXP)) { try { // decodes path segment URI url = new URI(trimmedDoi); trimmedDoi = url.getScheme() + "://" + url.getHost() + url.getPath(); } catch (URISyntaxException e) { - throw new IllegalArgumentException(doi + " is not a valid HTTP DOI."); + throw new IllegalArgumentException(doi + " is not a valid HTTP DOI/Short DOI."); } } - // Extract DOI + // Extract DOI/Short DOI Matcher matcher = EXACT_DOI_PATT.matcher(trimmedDoi); if (matcher.find()) { // match only group \1 this.doi = matcher.group(1); } else { - throw new IllegalArgumentException(trimmedDoi + " is not a valid DOI."); + // Short DOI + Matcher shortDoiMatcher = EXACT_SHORT_DOI_PATT.matcher(trimmedDoi); + if (shortDoiMatcher.find()) { + this.doi = shortDoiMatcher.group(1); + isShortDoi = true; + } else { + throw new IllegalArgumentException(trimmedDoi + " is not a valid DOI/Short DOI."); + } } } /** * Creates an Optional from various schemes including URL, URN, and plain DOIs. * - * Useful for suppressing the IllegalArgumentException of the Constructor - * and checking for Optional.isPresent() instead. + * Useful for suppressing the IllegalArgumentException of the Constructor and checking for + * Optional.isPresent() instead. * - * @param doi the DOI string + * @param doi the DOI/Short DOI string * @return an Optional containing the DOI or an empty Optional */ public static Optional parse(String doi) { @@ -105,9 +139,9 @@ public static Optional parse(String doi) { } /** - * Determines whether a DOI is valid or not + * Determines whether a DOI/Short DOI is valid or not * - * @param doi the DOI string + * @param doi the DOI/Short DOI string * @return true if DOI is valid, false otherwise */ public static boolean isValid(String doi) { @@ -115,9 +149,9 @@ public static boolean isValid(String doi) { } /** - * Tries to find a DOI inside the given text. + * Tries to find a DOI/Short DOI inside the given text. * - * @param text the Text which might contain a DOI + * @param text the Text which might contain a DOI/Short DOI * @return an Optional containing the DOI or an empty Optional */ public static Optional findInText(String text) { @@ -128,6 +162,12 @@ public static Optional findInText(String text) { // match only group \1 result = Optional.of(new DOI(matcher.group(1))); } + + matcher = SHORT_DOI_PATT.matcher(text); + if (matcher.find()) { + result = Optional.of(new DOI(matcher.group(1))); + } + return result; } @@ -139,18 +179,27 @@ public String toString() { } /** - * Return the plain DOI + * Return the plain DOI/Short DOI * - * @return the plain DOI value. + * @return the plain DOI/Short DOI value. */ public String getDOI() { return doi; } /** - * Return a URI presentation for the DOI + * Determines whether DOI is short DOI or not + * + * @return true if DOI is short DOI, false otherwise + */ + public boolean isShortDoi() { + return isShortDoi; + } + + /** + * Return a URI presentation for the DOI/Short DOI * - * @return an encoded URI representation of the DOI + * @return an encoded URI representation of the DOI/Short DOI */ @Override public Optional getExternalURI() { @@ -165,9 +214,9 @@ public Optional getExternalURI() { } /** - * Return an ASCII URL presentation for the DOI + * Return an ASCII URL presentation for the DOI/Short DOI * - * @return an encoded URL representation of the DOI + * @return an encoded URL representation of the DOI/Short DOI */ public String getURIAsASCIIString() { return getExternalURI().map(URI::toASCIIString).orElse(""); diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 639970999d2..730ccf0e8bb 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1555,6 +1555,8 @@ Add\ enclosing\ braces=Add enclosing braces Add\ braces\ encapsulating\ the\ complete\ field\ content.=Add braces encapsulating the complete field content. Remove\ enclosing\ braces=Remove enclosing braces Removes\ braces\ encapsulating\ the\ complete\ field\ content.=Removes braces encapsulating the complete field content. +Shorten\ DOI=Shorten DOI +Shortens\ DOI\ to\ more\ human\ readable\ form.=Shortens DOI to more human readable form. Sentence\ case=Sentence case Shortens\ lists\ of\ persons\ if\ there\ are\ more\ than\ 2\ persons\ to\ "et\ al.".=Shortens lists of persons if there are more than 2 persons to "et al.". Title\ case=Title case diff --git a/src/test/java/org/jabref/logic/formatter/bibtexfields/ShortenDOIFormatterTest.java b/src/test/java/org/jabref/logic/formatter/bibtexfields/ShortenDOIFormatterTest.java new file mode 100644 index 00000000000..1191014af41 --- /dev/null +++ b/src/test/java/org/jabref/logic/formatter/bibtexfields/ShortenDOIFormatterTest.java @@ -0,0 +1,24 @@ +package org.jabref.logic.formatter.bibtexfields; + +import org.jabref.testutils.category.FetcherTest; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@FetcherTest +class ShortenDOIFormatterTest { + + private ShortenDOIFormatter formatter; + + @BeforeEach + public void setUp() { + formatter = new ShortenDOIFormatter(); + } + + @Test + public void formatDoi() { + assertEquals("10/adc", formatter.format("10.1006/jmbi.1998.2354")); + } +} diff --git a/src/test/java/org/jabref/logic/importer/util/ShortDOIServiceTest.java b/src/test/java/org/jabref/logic/importer/util/ShortDOIServiceTest.java new file mode 100644 index 00000000000..26ea4706030 --- /dev/null +++ b/src/test/java/org/jabref/logic/importer/util/ShortDOIServiceTest.java @@ -0,0 +1,36 @@ +package org.jabref.logic.importer.util; + +import org.jabref.model.entry.identifier.DOI; +import org.jabref.testutils.category.FetcherTest; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@FetcherTest +class ShortDOIServiceTest { + + private final DOI doi = new DOI("10.1109/ACCESS.2013.2260813"); + private final DOI notExistingDoi = new DOI("10.1109/ACCESS.2013.226081400"); + + private ShortDOIService sut; + + @BeforeEach + void setUp() { + sut = new ShortDOIService(); + } + + @Test + void getShortDOI() throws ShortDOIServiceException { + DOI shortDoi = sut.getShortDOI(doi); + + assertEquals("10/gf4gqc", shortDoi.getDOI()); + } + + @Test + void shouldThrowExceptionWhenDOIWasNotFound() throws ShortDOIServiceException { + assertThrows(ShortDOIServiceException.class, () -> sut.getShortDOI(notExistingDoi)); + } +} diff --git a/src/test/java/org/jabref/model/entry/identifier/DOITest.java b/src/test/java/org/jabref/model/entry/identifier/DOITest.java index 4d45bb239cf..44af0ec70a2 100644 --- a/src/test/java/org/jabref/model/entry/identifier/DOITest.java +++ b/src/test/java/org/jabref/model/entry/identifier/DOITest.java @@ -18,22 +18,42 @@ public void acceptPlainDoi() { assertEquals("10.1126/sciadv.1500214", new DOI("10.1126/sciadv.1500214").getDOI()); } + @Test + public void acceptPlainShortDoi() { + assertEquals("10/gf4gqc", new DOI("10/gf4gqc").getDOI()); + } + @Test public void ignoreLeadingAndTrailingWhitespaces() { assertEquals("10.1006/jmbi.1998.2354", new DOI(" 10.1006/jmbi.1998.2354 ").getDOI()); } + @Test + public void ignoreLeadingAndTrailingWhitespacesInShortDoi() { + assertEquals("10/gf4gqc", new DOI(" 10/gf4gqc ").getDOI()); + } + @Test public void rejectEmbeddedDoi() { assertThrows(IllegalArgumentException.class, () -> new DOI("other stuff 10.1006/jmbi.1998.2354 end")); } + @Test + public void rejectEmbeddedShortDoi() { + assertThrows(IllegalArgumentException.class, () -> new DOI("other stuff 10/gf4gqc end")); + } + @Test public void rejectInvalidDirectoryIndicator() { // wrong directory indicator assertThrows(IllegalArgumentException.class, () -> new DOI("12.1006/jmbi.1998.2354 end")); } + @Test + public void rejectInvalidDirectoryIndicatorInShortDoi() { + assertThrows(IllegalArgumentException.class, () -> new DOI("20/abcd")); + } + @Test public void rejectInvalidDoiUri() { assertThrows(IllegalArgumentException.class, () -> new DOI("https://thisisnouri")); @@ -45,12 +65,22 @@ public void rejectMissingDivider() { assertThrows(IllegalArgumentException.class, () -> new DOI("10.1006jmbi.1998.2354 end")); } + @Test + public void rejectMissingDividerInShortDoi() { + assertThrows(IllegalArgumentException.class, () -> new DOI("10gf4gqc end")); + } + @Test public void acceptDoiPrefix() { // Doi prefix assertEquals("10.1006/jmbi.1998.2354", new DOI("doi:10.1006/jmbi.1998.2354").getDOI()); } + @Test + public void acceptDoiPrefixInShortDoi() { + assertEquals("10/gf4gqc", new DOI("doi:10/gf4gqc").getDOI()); + } + @Test public void acceptURNPrefix() { assertEquals("10.123/456", new DOI("urn:10.123/456").getDOI()); @@ -60,6 +90,15 @@ public void acceptURNPrefix() { assertEquals("10.123:456ABC/zyz", new DOI("http://doi.org/urn:doi:10.123:456ABC%2Fzyz").getDOI()); } + @Test + public void acceptURNPrefixInShortDoi() { + assertEquals("10/gf4gqc", new DOI("urn:10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("urn:doi:10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("http://doi.org/urn:doi:10/gf4gqc").getDOI()); + // : is also allowed as divider, will be replaced by RESOLVER + assertEquals("10:gf4gqc", new DOI("http://doi.org/urn:doi:10:gf4gqc").getDOI()); + } + @Test public void acceptURLDoi() { // http @@ -87,6 +126,27 @@ public void acceptURLDoi() { new DOI("http://doi.ieeecomputersociety.org/10.1109/MIC.2012.43").getDOI()); } + @Test + public void acceptURLShortDoi() { + // http + assertEquals("10/gf4gqc", new DOI("http://doi.org/10/gf4gqc").getDOI()); + // https + assertEquals("10/gf4gqc", new DOI("https://doi.org/10/gf4gqc").getDOI()); + // https with % divider + assertEquals("10/gf4gqc", new DOI("https://dx.doi.org/10%2Fgf4gqc").getDOI()); + // other domains + assertEquals("10/gf4gqc", new DOI("http://doi.acm.org/10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("http://doi.acm.net/10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("http://doi.acm.com/10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("http://doi.acm.de/10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("http://dx.doi.org/10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("http://dx.doi.net/10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("http://dx.doi.com/10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("http://dx.doi.de/10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("http://dx.doi.org/10/gf4gqc").getDOI()); + assertEquals("10/gf4gqc", new DOI("http://doi.ieeecomputersociety.org/10/gf4gqc").getDOI()); + } + @Test public void correctlyDecodeHttpDOIs() { // See http://www.doi.org/doi_handbook/2_Numbering.html#2.5.2.4 @@ -133,12 +193,22 @@ public void constructCorrectURLForDoi() { new DOI("doi:10.1109/VLHCC.2004.20").getURIAsASCIIString()); } + @Test + public void constructCorrectURLForShortDoi() { + assertEquals("https://doi.org/10/gf4gqc", new DOI("10/gf4gqc").getURIAsASCIIString()); + } + @Test public void findDoiInsideArbitraryText() { assertEquals("10.1006/jmbi.1998.2354", DOI.findInText("other stuff 10.1006/jmbi.1998.2354 end").get().getDOI()); } + @Test + public void findShortDoiInsideArbitraryText() { + assertEquals("10/gf4gqc", DOI.findInText("other stuff 10/gf4gqc end").get().getDOI()); + } + @Test public void noDOIFoundInsideArbitraryText() { assertEquals(Optional.empty(), DOI.findInText("text without 28282 a doi")); @@ -149,4 +219,20 @@ public void parseDOIWithWhiteSpace() { String doiWithSpace = "https : / / doi.org / 10 .1109 /V LHCC.20 04.20"; assertEquals("https://doi.org/10.1109/VLHCC.2004.20", DOI.parse(doiWithSpace).get().getURIAsASCIIString()); } + + @Test + public void parseShortDOIWithWhiteSpace() { + String shortDoiWithSpace = "https : / / doi.org / 10 / gf4gqc"; + assertEquals("https://doi.org/10/gf4gqc", DOI.parse(shortDoiWithSpace).get().getURIAsASCIIString()); + } + + @Test + public void isShortDoiShouldReturnTrueWhenItIsShortDoi() { + assertEquals(true, new DOI("10/abcde").isShortDoi()); + } + + @Test + public void isShortDoiShouldReturnFalseWhenItIsDoi() { + assertEquals(false, new DOI("10.1006/jmbi.1998.2354").isShortDoi()); + } } From 3b85906fecb172ac3bbda5ac46f73ebe1f445c38 Mon Sep 17 00:00:00 2001 From: omer-rotem1 Date: Sun, 8 Sep 2019 03:55:17 -0700 Subject: [PATCH 055/560] unifying month format across all bib files (#5286) --- .../org/jabref/logic/importer/fileformat/RisImporterTest3.bib | 2 +- .../logic/importer/fileformat/RisImporterTestDate1.1.bib | 4 ++-- .../logic/importer/fileformat/RisImporterTestDate1.2.bib | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTest3.bib b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTest3.bib index 642578e2d77..9b09a156bc1 100644 --- a/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTest3.bib +++ b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTest3.bib @@ -5,7 +5,7 @@ @incollection{ issn = {978-1-4822-5326-9}, journal = {Dermoscopy Image Analysis}, number = {0}, - month = {#sep#}, + month = sep, pages = {1-22}, publisher = {CRC Press}, series = {Digital Imaging and Computer Vision}, diff --git a/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestDate1.1.bib b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestDate1.1.bib index 9be8fcc17a6..a9553c322a0 100644 --- a/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestDate1.1.bib +++ b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestDate1.1.bib @@ -4,7 +4,7 @@ @article{ doi = {10.1063/1.1954747}, issn = {0021-9606}, journal = {J. Chem. Phys.}, - month = {#jul#}, + month = jul, number = {3}, pages = {034708}, publisher = {American Institute of Physics}, @@ -12,4 +12,4 @@ @article{ url = {https://doi.org/10.1063/1.1954747}, volume = {123}, year = {2005} -} \ No newline at end of file +} diff --git a/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestDate1.2.bib b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestDate1.2.bib index 9be8fcc17a6..a9553c322a0 100644 --- a/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestDate1.2.bib +++ b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestDate1.2.bib @@ -4,7 +4,7 @@ @article{ doi = {10.1063/1.1954747}, issn = {0021-9606}, journal = {J. Chem. Phys.}, - month = {#jul#}, + month = jul, number = {3}, pages = {034708}, publisher = {American Institute of Physics}, @@ -12,4 +12,4 @@ @article{ url = {https://doi.org/10.1063/1.1954747}, volume = {123}, year = {2005} -} \ No newline at end of file +} From 8bb6380e177006fd798621809ba6d99314090615 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 8 Sep 2019 15:45:03 +0200 Subject: [PATCH 056/560] Fix main class name (#5251) * Fix main class name Reverts https://github.com/JabRef/jabref/commit/ba60cbe18640611e7d7f0986314c82049668c5f6. According to https://github.com/java9-modularity/gradle-modules-plugin#using-the-application-plugin this is the correct way to specify the main class name. * Update build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 47c57be802e..b60f4f279d8 100644 --- a/build.gradle +++ b/build.gradle @@ -55,7 +55,7 @@ project.ext.threeDotVersion = "5.0.0.1" project.ext.install4jDir = hasProperty("install4jDir") ? getProperty("install4jDir") : (OperatingSystem.current().isWindows() ? 'C:/Program Files/install4j8' : 'install4j8') sourceCompatibility = 11 targetCompatibility = 11 -mainClassName = "org.jabref.JabRefLauncher" +mainClassName = "$moduleName/org.jabref.JabRefLauncher" // TODO: Ugly workaround to temporarily ignore build errors to dependencies of latex2unicode // These should be removed, as well as the files in the lib folder, as soon as they have valid module names From 373b9f5f582729f2e5cb12d03f139eac67d4c7d3 Mon Sep 17 00:00:00 2001 From: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> Date: Sun, 8 Sep 2019 16:56:06 +0200 Subject: [PATCH 057/560] Conversion of Preferences/TableColumnsTab to mvvm (#5185) * Initial * Added contents of the list and bindings * Initial * Added contents of the list and bindings * Refactored specialFields * Added checking, storing and specialFields * Added add and remove custom column, refactor * Fixes unintentional sorting, added moving columns up and down * Refactoring, added update of MainTable * Refactored to TableView, Added ComboBox * Fixed bug in ui update * Refactor and rewording to clean up * Changed table order reload icon * Refactor to save special fields column order, rewording * Refactor mvvm * Removed now superfluous restart message * l10n * Refactor for order * Added validation, cleanup, and minor fix * Refactored FieldsUtil for reuse * Refactored FieldsUtil for reuse * Fixes and cleanups * Refactor and cleanups * Refactor to streams * l10n * l10n again * Cleanups * Fix sorting bug --- src/main/java/org/jabref/gui/BasePanel.java | 4 +- src/main/java/org/jabref/gui/JabRefFrame.java | 44 +- .../gui/maintable/ColumnPreferences.java | 48 +- .../gui/maintable/MainTableColumnFactory.java | 39 +- .../jabref/gui/maintable/RightClickMenu.java | 29 +- .../PreferencesDialogViewModel.java | 2 +- .../preferences/TableColumnsItemModel.java | 90 +++ .../gui/preferences/TableColumnsTab.fxml | 113 ++++ .../gui/preferences/TableColumnsTab.java | 562 ------------------ .../gui/preferences/TableColumnsTabView.java | 145 +++++ .../preferences/TableColumnsTabViewModel.java | 274 +++++++++ .../preferences/XmpPrivacyTabViewModel.java | 2 +- .../jabref/preferences/JabRefPreferences.java | 95 +-- src/main/resources/l10n/JabRef_en.properties | 30 +- 14 files changed, 733 insertions(+), 744 deletions(-) create mode 100644 src/main/java/org/jabref/gui/preferences/TableColumnsItemModel.java create mode 100644 src/main/java/org/jabref/gui/preferences/TableColumnsTab.fxml delete mode 100644 src/main/java/org/jabref/gui/preferences/TableColumnsTab.java create mode 100644 src/main/java/org/jabref/gui/preferences/TableColumnsTabView.java create mode 100644 src/main/java/org/jabref/gui/preferences/TableColumnsTabViewModel.java diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java index ab8631489d5..9c2f2ddb6d2 100644 --- a/src/main/java/org/jabref/gui/BasePanel.java +++ b/src/main/java/org/jabref/gui/BasePanel.java @@ -121,12 +121,12 @@ public class BasePanel extends StackPane { // Keeps track of the string dialog if it is open. private final Map actions = new HashMap<>(); private final SidePaneManager sidePaneManager; - private final BasePanelPreferences preferences; private final ExternalFileTypes externalFileTypes; private final EntryEditor entryEditor; private final DialogService dialogService; private MainTable mainTable; + private BasePanelPreferences preferences; // To contain instantiated entry editors. This is to save time // As most enums, this must not be null private BasePanelMode mode = BasePanelMode.SHOWING_NOTHING; @@ -727,6 +727,8 @@ public void actionPerformed(ActionEvent e) { } public void setupMainPanel() { + preferences = BasePanelPreferences.from(Globals.prefs); + splitPane = new SplitPane(); splitPane.setOrientation(Orientation.VERTICAL); adjustSplitter(); // restore last splitting state (before mainTable is created as creation affects the stored size of the entryEditors) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index d8304f472cd..74c2e327603 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -694,41 +694,17 @@ private MenuBar createMenu() { ); if (Globals.prefs.getBoolean(JabRefPreferences.SPECIALFIELDSENABLED)) { - boolean menuItemAdded = false; - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_RANKING)) { - edit.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.RANKING, factory, undoManager)); - menuItemAdded = true; - } - - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_RELEVANCE)) { - edit.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.RELEVANCE, factory)); - menuItemAdded = true; - } - - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_QUALITY)) { - edit.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.QUALITY, factory)); - menuItemAdded = true; - } - - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_PRINTED)) { - edit.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.PRINTED, factory)); - menuItemAdded = true; - } - - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_PRIORITY)) { - edit.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.PRIORITY, factory, undoManager)); - menuItemAdded = true; - } - - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_READ)) { - edit.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.READ_STATUS, factory, undoManager)); - menuItemAdded = true; - } - - if (menuItemAdded) { - edit.getItems().add(new SeparatorMenuItem()); - } + edit.getItems().addAll( + SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.RANKING, factory, undoManager), + SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.RELEVANCE, factory), + SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.QUALITY, factory), + SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.PRINTED, factory), + SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.PRIORITY, factory, undoManager), + SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.READ_STATUS, factory, undoManager), + new SeparatorMenuItem() + ); } + //@formatter:off library.getItems().addAll( factory.createMenuItem(StandardActions.NEW_ENTRY, new NewEntryAction(this, dialogService, Globals.prefs, stateManager)), diff --git a/src/main/java/org/jabref/gui/maintable/ColumnPreferences.java b/src/main/java/org/jabref/gui/maintable/ColumnPreferences.java index c02ab26df61..f2a8364f7c7 100644 --- a/src/main/java/org/jabref/gui/maintable/ColumnPreferences.java +++ b/src/main/java/org/jabref/gui/maintable/ColumnPreferences.java @@ -5,29 +5,31 @@ import javafx.scene.control.TableColumn.SortType; -import org.jabref.model.entry.field.SpecialField; - public class ColumnPreferences { public static final double DEFAULT_FIELD_LENGTH = 100; private final boolean showFileColumn; private final boolean showUrlColumn; private final boolean preferDoiOverUrl; - private final boolean showEprintColumn; - private final List normalColumns; - private final List specialFieldColumns; - private final List extraFileColumns; + private final boolean showEPrintColumn; + private final List columnNames; + private final boolean specialFieldsEnabled; + private final boolean autoSyncSpecialFieldsToKeyWords; + private final boolean serializeSpecialFields; + private final boolean extraFileColumnsEnabled; private final Map columnWidths; private final Map columnSortType; - public ColumnPreferences(boolean showFileColumn, boolean showUrlColumn, boolean preferDoiOverUrl, boolean showEprintColumn, List normalColumns, List specialFieldColumns, List extraFileColumns, Map columnWidths, Map columnSortType) { + public ColumnPreferences(boolean showFileColumn, boolean showUrlColumn, boolean preferDoiOverUrl, boolean showEPrintColumn, List columnNames, boolean specialFieldsEnabled, boolean autoSyncSpecialFieldsToKeyWords, boolean serializeSpecialFields, boolean extraFileColumnsEnabled, Map columnWidths, Map columnSortType) { this.showFileColumn = showFileColumn; this.showUrlColumn = showUrlColumn; this.preferDoiOverUrl = preferDoiOverUrl; - this.showEprintColumn = showEprintColumn; - this.normalColumns = normalColumns; - this.specialFieldColumns = specialFieldColumns; - this.extraFileColumns = extraFileColumns; + this.showEPrintColumn = showEPrintColumn; + this.columnNames = columnNames; + this.specialFieldsEnabled = specialFieldsEnabled; + this.autoSyncSpecialFieldsToKeyWords = autoSyncSpecialFieldsToKeyWords; + this.serializeSpecialFields = serializeSpecialFields; + this.extraFileColumnsEnabled = extraFileColumnsEnabled; this.columnWidths = columnWidths; this.columnSortType = columnSortType; } @@ -45,22 +47,30 @@ public boolean preferDoiOverUrl() { } public boolean showEprintColumn() { - return showEprintColumn; + return showEPrintColumn; } - public List getExtraFileColumns() { - return extraFileColumns; + public boolean getSpecialFieldsEnabled() { return specialFieldsEnabled; } + + public boolean getAutoSyncSpecialFieldsToKeyWords() { + return autoSyncSpecialFieldsToKeyWords; } - public List getSpecialFieldColumns() { - return specialFieldColumns; + public boolean getSerializeSpecialFields() { + return serializeSpecialFields; + } + + public boolean getExtraFileColumnsEnabled() { return extraFileColumnsEnabled; } + + public List getColumnNames() { + return columnNames; } - public List getNormalColumns() { - return normalColumns; + public Map getColumnWidths() { + return columnWidths; } - public double getPrefColumnWidth(String columnName) { + public double getColumnWidth(String columnName) { return columnWidths.getOrDefault(columnName, DEFAULT_FIELD_LENGTH); } diff --git a/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java b/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java index 2c2845638e5..45fc7c7b72c 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java +++ b/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java @@ -33,6 +33,7 @@ import org.jabref.gui.icon.JabRefIcon; import org.jabref.gui.specialfields.SpecialFieldValueViewModel; import org.jabref.gui.specialfields.SpecialFieldViewModel; +import org.jabref.gui.util.FieldsUtil; import org.jabref.gui.util.OptionalValueTableCellFactory; import org.jabref.gui.util.ValueTableCellFactory; import org.jabref.gui.util.comparator.RankingFieldComparator; @@ -81,12 +82,11 @@ public MainTableColumnFactory(BibDatabaseContext database, ColumnPreferences pre public List> createColumns() { List> columns = new ArrayList<>(); columns.add(createGroupColumn()); - // Add column for linked files + if (preferences.showFileColumn()) { columns.add(createFileColumn()); } - // Add column for DOI/URL if (preferences.showUrlColumn()) { if (preferences.preferDoiOverUrl()) { columns.add(createUrlOrDoiColumn(IconTheme.JabRefIcons.DOI, StandardField.DOI, StandardField.URL)); @@ -95,21 +95,19 @@ public MainTableColumnFactory(BibDatabaseContext database, ColumnPreferences pre } } - // Add column for eprints if (preferences.showEprintColumn()) { columns.add(createEprintColumn(IconTheme.JabRefIcons.WWW, StandardField.EPRINT)); } - // Add columns for other file types - columns.addAll(preferences.getExtraFileColumns().stream().map(this::createExtraFileColumn).collect(Collectors.toList())); - - // Add 'normal' bibtex fields as configured in the preferences - columns.addAll(createNormalColumns()); - - // Add the "special" icon columns (e.g., ranking, file, ...) that are enabled in preferences - for (SpecialField field : preferences.getSpecialFieldColumns()) { - columns.add(createSpecialFieldColumn((field))); - } + preferences.getColumnNames().stream().map(FieldFactory::parseField).forEach(field -> { + if (field instanceof FieldsUtil.ExtraFilePseudoField) { + columns.add(createExtraFileColumn(field.getName())); + } else if (field instanceof SpecialField) { + columns.add(createSpecialFieldColumn((SpecialField) field)); + } else { + columns.add(createNormalColumn(field)); + } + }); return columns; } @@ -163,20 +161,15 @@ private Node createGroupColorRegion(BibEntryTableViewModel entry, List> createNormalColumns() { - List> columns = new ArrayList<>(); - - // Read table columns from preferences - for (String columnName : preferences.getNormalColumns()) { - NormalTableColumn column = new NormalTableColumn(columnName, FieldFactory.parseOrFields(columnName), database.getDatabase()); + private TableColumn createNormalColumn(Field field) { + String columnName = field.getName(); + NormalTableColumn column = new NormalTableColumn(columnName, FieldFactory.parseOrFields(columnName), database.getDatabase()); new ValueTableCellFactory() .withText(text -> text) .install(column); column.setSortable(true); - column.setPrefWidth(preferences.getPrefColumnWidth(columnName)); - columns.add(column); - } - return columns; + column.setPrefWidth(preferences.getColumnWidth(columnName)); + return column; } private TableColumn> createSpecialFieldColumn(SpecialField specialField) { diff --git a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java index dbabf52e37e..7029810a2fa 100644 --- a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java +++ b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java @@ -48,29 +48,12 @@ public static ContextMenu create(BibEntryTableViewModel entry, KeyBindingReposit contextMenu.getItems().add(new SeparatorMenuItem()); if (Globals.prefs.getBoolean(JabRefPreferences.SPECIALFIELDSENABLED)) { - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_RANKING)) { - contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, panel)); - } - - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_RELEVANCE)) { - contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, panel)); - } - - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_QUALITY)) { - contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.QUALITY, factory, panel)); - } - - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_PRINTED)) { - contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.PRINTED, factory, panel)); - } - - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_PRIORITY)) { - contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.PRIORITY, factory, panel)); - } - - if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_READ)) { - contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.READ_STATUS, factory, panel)); - } + contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, panel)); + contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, panel)); + contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.QUALITY, factory, panel)); + contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.PRINTED, factory, panel)); + contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.PRIORITY, factory, panel)); + contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.READ_STATUS, factory, panel)); } contextMenu.getItems().add(new SeparatorMenuItem()); diff --git a/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java b/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java index d1a64d69c58..94d9af78de5 100644 --- a/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java @@ -49,7 +49,7 @@ public PreferencesDialogViewModel(DialogService dialogService, TaskExecutor task new GeneralTabView(prefs), new FileTabView(prefs), new TablePrefsTab(prefs), - new TableColumnsTab(prefs, frame), + new TableColumnsTabView(prefs), new PreviewTabView(prefs), new ExternalTabView(prefs, frame), new GroupsTabView(prefs), diff --git a/src/main/java/org/jabref/gui/preferences/TableColumnsItemModel.java b/src/main/java/org/jabref/gui/preferences/TableColumnsItemModel.java new file mode 100644 index 00000000000..9ac5faa8fb7 --- /dev/null +++ b/src/main/java/org/jabref/gui/preferences/TableColumnsItemModel.java @@ -0,0 +1,90 @@ +package org.jabref.gui.preferences; + +import java.util.Objects; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.beans.value.ObservableValue; + +import org.jabref.gui.maintable.ColumnPreferences; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.UnknownField; + +public class TableColumnsItemModel { + + private final ObjectProperty field; + private final StringProperty name = new SimpleStringProperty(""); + private final DoubleProperty length = new SimpleDoubleProperty(ColumnPreferences.DEFAULT_FIELD_LENGTH); + private final BooleanProperty editableProperty = new SimpleBooleanProperty(true); + + public TableColumnsItemModel() { + this.field = new SimpleObjectProperty<>(new UnknownField(Localization.lang("New column"))); + } + + public TableColumnsItemModel(Field field) { + this.field = new SimpleObjectProperty<>(field); + this.editableProperty.setValue(this.field.get() instanceof UnknownField); + } + + public TableColumnsItemModel(Field field, double length) { + this.field = new SimpleObjectProperty<>(field); + this.length.setValue(length); + this.editableProperty.setValue(this.field.get() instanceof UnknownField); + } + + public void setField(Field field) { + this.field.set(field); + } + + public Field getField() { + return field.get(); + } + + public ObservableValue fieldProperty() { return this.field; } + + public void setName(String name) { + if (editableProperty.get()) { + field.setValue(new UnknownField(name)); + } + } + + public String getName() { + return field.get().getName(); + } + + public StringProperty nameProperty() { return this.name; } + + public void setLength(double length) { + this.length.set(length); + } + + public double getLength() { + return length.get(); + } + + public DoubleProperty lengthProperty() { return this.length; } + + public ReadOnlyBooleanProperty editableProperty() { return editableProperty; } + + @Override + public int hashCode() { + return Objects.hash(field); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TableColumnsItemModel) { + return Objects.equals(this.field, ((TableColumnsItemModel) obj).field); + } else { + return false; + } + } +} diff --git a/src/main/java/org/jabref/gui/preferences/TableColumnsTab.fxml b/src/main/java/org/jabref/gui/preferences/TableColumnsTab.fxml new file mode 100644 index 00000000000..69912ee367c --- /dev/null +++ b/src/main/java/org/jabref/gui/preferences/TableColumnsTab.fxml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + +