From d969b76b875d9b50902793bab9595c8f6ace454b Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 14 Aug 2022 22:30:43 +0200 Subject: [PATCH 1/5] Restructure development documation --- docs/advanced-reading/index.md | 5 - .../code-quality.md | 2 +- .../custom-svg-icons.md | 4 +- docs/code-howtos/error-handling.md | 40 ++ docs/code-howtos/eventbus.md | 71 +++ .../fetchers.md | 4 +- docs/code-howtos/index.md | 106 +++++ .../javafx.md | 52 ++- .../jpackage.md | 6 +- docs/code-howtos/localization.md | 52 +++ docs/code-howtos/logging.md | 24 + .../remote-storage.md | 4 +- .../telemetry.md | 4 +- docs/code-howtos/testing.md | 132 ++++++ .../tools.md | 4 +- .../ui-recommendations.md | 6 +- docs/getting-into-the-code/code-howtos.md | 431 ------------------ .../development-strategy.md | 4 +- ...elines-for-setting-up-a-local-workspace.md | 4 +- .../high-level-documentation.md | 2 +- docs/getting-into-the-code/testing.md | 31 -- docs/readings-on-coding/index.md | 5 - 22 files changed, 496 insertions(+), 497 deletions(-) delete mode 100644 docs/advanced-reading/index.md rename docs/{advanced-reading => code-howtos}/code-quality.md (98%) rename docs/{advanced-reading => code-howtos}/custom-svg-icons.md (98%) create mode 100644 docs/code-howtos/error-handling.md create mode 100644 docs/code-howtos/eventbus.md rename docs/{advanced-reading => code-howtos}/fetchers.md (99%) create mode 100644 docs/code-howtos/index.md rename docs/{readings-on-coding => code-howtos}/javafx.md (61%) rename docs/{advanced-reading => code-howtos}/jpackage.md (95%) create mode 100644 docs/code-howtos/localization.md create mode 100644 docs/code-howtos/logging.md rename docs/{advanced-reading => code-howtos}/remote-storage.md (96%) rename docs/{advanced-reading => code-howtos}/telemetry.md (95%) create mode 100644 docs/code-howtos/testing.md rename docs/{readings-on-coding => code-howtos}/tools.md (98%) rename docs/{advanced-reading => code-howtos}/ui-recommendations.md (95%) delete mode 100644 docs/getting-into-the-code/code-howtos.md delete mode 100644 docs/getting-into-the-code/testing.md delete mode 100644 docs/readings-on-coding/index.md diff --git a/docs/advanced-reading/index.md b/docs/advanced-reading/index.md deleted file mode 100644 index bb31c245120..00000000000 --- a/docs/advanced-reading/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -nav_order: 5 -has_children: true ---- -# Advanced Reading diff --git a/docs/advanced-reading/code-quality.md b/docs/code-howtos/code-quality.md similarity index 98% rename from docs/advanced-reading/code-quality.md rename to docs/code-howtos/code-quality.md index 0cd041920cf..30d55d56f72 100644 --- a/docs/advanced-reading/code-quality.md +++ b/docs/code-howtos/code-quality.md @@ -1,6 +1,6 @@ --- +parent: Code Howtos nav_order: 1 -parent: Advanced Reading --- # Code Quality diff --git a/docs/advanced-reading/custom-svg-icons.md b/docs/code-howtos/custom-svg-icons.md similarity index 98% rename from docs/advanced-reading/custom-svg-icons.md rename to docs/code-howtos/custom-svg-icons.md index a0bccc0622c..6e94b1dbee3 100644 --- a/docs/advanced-reading/custom-svg-icons.md +++ b/docs/code-howtos/custom-svg-icons.md @@ -1,6 +1,6 @@ --- -nav_order: 3 -parent: Advanced Reading +parent: Code Howtos +nav_order: 2 --- # Custom SVG icons diff --git a/docs/code-howtos/error-handling.md b/docs/code-howtos/error-handling.md new file mode 100644 index 00000000000..2df80219ed0 --- /dev/null +++ b/docs/code-howtos/error-handling.md @@ -0,0 +1,40 @@ +--- +parent: Code Howtos +nav_order: 3 +--- +# Error Handling in JabRef + +## Throwing and Catching Exceptions + +Principles: + +* All exceptions we throw should be or extend `JabRefException`; This is especially important if the message stored in the Exception should be shown to the user. `JabRefException` has already implemented the `getLocalizedMessage()` method which should be used for such cases (see details below!). +* Catch and wrap all API exceptions (such as `IOExceptions`) and rethrow them + * Example: + + ```java + try { + // ... + } catch (IOException ioe) { + throw new JabRefException("Something went wrong...", + Localization.lang("Something went wrong...", ioe); + } + ``` +* Never, ever throw and catch `Exception` or `Throwable` +* Errors should only be logged when they are finally caught (i.e., logged only once). See **Logging** for details. +* If the Exception message is intended to be shown to the User in the UI (see below) provide also a localizedMessage (see `JabRefException`). + +_(Rationale and further reading:_ [https://www.baeldung.com/java-exceptions](https://www.baeldung.com/java-exceptions)_)_ + +## Outputting Errors in the UI + +Principle: Error messages shown to the User should not contain technical details (e.g., underlying exceptions, or even stack traces). Instead, the message should be concise, understandable for non-programmers and localized. The technical reasons (and stack traces) for a failure should only be logged. + +To show error message two different ways are usually used in JabRef: + +* showing an error dialog +* updating the status bar at the bottom of the main window + +``` +TODO: Usage of status bar and Swing Dialogs +``` diff --git a/docs/code-howtos/eventbus.md b/docs/code-howtos/eventbus.md new file mode 100644 index 00000000000..efb862335d1 --- /dev/null +++ b/docs/code-howtos/eventbus.md @@ -0,0 +1,71 @@ +--- +parent: Code Howtos +nav_order: 4 +--- +# Using the EventSystem + +## What the EventSystem is used for + +Many times there is a need to provide an object on many locations simultaneously. This design pattern is quite similar to Java's Observer, but it is much simpler and readable while having the same functional sense. + +## Main principle + +`EventBus` represents a communication line between multiple components. Objects can be passed through the bus and reach the listening method of another object which is registered on that `EventBus` instance. Hence, the passed object is available as a parameter in the listening method. + +## Register to the `EventBus` + +Any listening method has to be annotated with `@Subscribe` keyword and must have only one accepting parameter. Furthermore, the object which contains such listening method(s) has to be registered using the `register(Object)` method provided by `EventBus`. The listening methods can be overloaded by using different parameter types. + +## Posting an object + +`post(object)` posts an object through the `EventBus` which has been used to register the listening/subscribing methods. + +## Short example + +```java +/* Listener.java */ + +import com.google.common.eventbus.Subscribe; + +public class Listener { + + private int value = 0; + + @Subscribe + public void listen(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } +} +``` + +```java +/* Main.java */ + +import com.google.common.eventbus.EventBus; + +public class Main { + private static EventBus eventBus = new EventBus(); + + public static void main(String[] args) { + Main main = new Main(); + Listener listener = new Listener(); + eventBus.register(listener); + eventBus.post(1); // 1 represents the passed event + + // Output should be 1 + System.out.println(listener.getValue()); + } +} +``` + +## Event handling in JabRef + +The `event` package contains some specific events which occur in JabRef. + +For example: Every time an entry was added to the database a new `EntryAddedEvent` is sent through the `eventBus` which is located in `BibDatabase`. + +If you want to catch the event you'll have to register your listener class with the `registerListener(Object listener)` method in `BibDatabase`. `EntryAddedEvent` provides also methods to get the inserted `BibEntry`. diff --git a/docs/advanced-reading/fetchers.md b/docs/code-howtos/fetchers.md similarity index 99% rename from docs/advanced-reading/fetchers.md rename to docs/code-howtos/fetchers.md index b1dc9cb4ca5..446151eff21 100644 --- a/docs/advanced-reading/fetchers.md +++ b/docs/code-howtos/fetchers.md @@ -1,6 +1,6 @@ --- -nav_order: 7 -parent: Advanced Reading +parent: Code Howtos +nav_order: 5 --- # Working on fetchers diff --git a/docs/code-howtos/index.md b/docs/code-howtos/index.md new file mode 100644 index 00000000000..94ca60ff51a --- /dev/null +++ b/docs/code-howtos/index.md @@ -0,0 +1,106 @@ +--- +nav_order: 6 +has_children: true +--- +# Code Howtos + +This page provides some development support in the form of howtos. +See also [High Level Documentation](../getting-into-thecode/high-level-documentation.md). + +## Generic code how tos + +We really recommend reading the book [Java by Comparison](http://java.by-comparison.com). + +Please read [https://github.com/cxxr/better-java](https://github.com/cxxr/better-java) + +* try not to abbreviate names of variables, classes or methods +* use lowerCamelCase instead of snake\_case +* name enums in singular, e.g. `Weekday` instead of `Weekdays` (except if they represent flags) + +## Cleanup and Formatters + +We try to build a cleanup mechanism based on formatters. The idea is that we can register these actions in arbitrary places, e.g., onSave, onImport, onExport, cleanup, etc. and apply them to different fields. The formatters themselves are independent of any logic and therefore easy to test. + +Example: [NormalizePagesFormatter](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/formatter/bibtexfields/NormalizePagesFormatter.java) + +## Drag and Drop + +Drag and Drop makes usage of the Dragboard. For JavaFX the following [tutorial](https://docs.oracle.com/javafx/2/drag\_drop/jfxpub-drag\_drop.htm) is helpful. Note that the data has to be serializable which is put on the dragboard. For drag and drop of Bib-entries between the maintable and the groups panel, a custom Dragboard is used, `CustomLocalDragboard` which is a generic alternative to the system one. + +For accessing or putting data into the Clipboard use the `ClipboardManager`. + +## Get the JabRef frame panel + +`JabRefFrame` and `BasePanel` are the two main classes. You should never directly call them, instead pass them as parameters to the class. + +## Get Absolute Filename or Path for file in File directory + +```java +Optional file = FileHelper.expandFilename(database, fileText, preferences.getFilePreferences()); +``` + +`String path` Can be the files name or a relative path to it. The Preferences should only be directly accessed in the GUI. For the usage in logic pass them as parameter + +## Setting a Database Directory for a .bib File + +* `@comment{jabref-meta: fileDirectory:` +* “fileDirectory” is determined by Globals.pref.get(“userFileDir”) (which defaults to “fileDirectory” +* There is also “fileDirectory-\”, which is determined by Globals.prefs.get(“userFileDirIndividual”) +* Used at DatabasePropertiesDialog + +## How to work with Preferences + +`model` and `logic` must not know `JabRefPreferences`. See `ProxyPreferences` for encapsulated preferences and [https://github.com/JabRef/jabref/pull/658](https://github.com/JabRef/jabref/pull/658) for a detailed discussion. + +See [https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/preferences/TimestampPreferences.java](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/preferences/TimestampPreferences.java) (via [https://github.com/JabRef/jabref/pull/3092](https://github.com/JabRef/jabref/pull/3092)) for the current way how to deal with preferences. + +Defaults should go into the model package. See [Comments in this Commit](https://github.com/JabRef/jabref/commit/2f553e6557bddf7753b618b0f4edcaa6e873f719#commitcomment-15779484) + +## UI + +Global variables should be avoided. Try to pass them as dependency. + +## "Special Fields" + +### keywords sync + +Database.addDatabaseChangeListener does not work as the DatabaseChangedEvent does not provide the field information. Therefore, we have to use BibtexEntry.addPropertyChangeListener(VetoableChangeListener listener) + +## Working with BibTeX data + +### Working with authors + +You can normalize the authors using `org.jabref.model.entry.AuthorList.fixAuthor_firstNameFirst(String)`. Then the authors always look nice. The only alternative containing all data of the names is `org.jabref.model.entry.AuthorList.fixAuthor_lastNameFirst(String)`. The other `fix...` methods omit data (like the von parts or the junior information). + +## Benchmarks + +* Benchmarks can be executed by running the `jmh` gradle task (this functionality uses the [JMH Gradle plugin](https://github.com/melix/jmh-gradle-plugin)) +* Best practices: + * Read test input from `@State` objects + * Return result of calculations (either explicitly or via a `BlackHole` object) +* [List of examples](https://github.com/melix/jmh-gradle-example/tree/master/src/jmh/java/org/openjdk/jmh/samples) + +## Measure performance + +Try out the [YourKit JAva Profiler](https://www.yourkit.com). + +## equals + +When creating an `equals` method follow: + +1. Use the `==` operator to check if the argument is a reference to this object. If so, return `true`. +2. Use the `instanceof` operator to check if the argument has the correct type. If not, return `false`. +3. Cast the argument to the correct type. +4. For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object. If all these tests succeed, return `true` otherwise, return `false`. +5. When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent? + +Also, note: + +* Always override `hashCode` when you override equals (`hashCode` also has very strict rules) (Item 9 of[Effective Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/)) +* Don’t try to be too clever +* Don’t substitute another type for `Object` in the equals declaration + +## Files and Paths + +Always try to use the methods from the nio-package. For interoperability, they provide methods to convert between file and path. [https://docs.oracle.com/javase/tutorial/essential/io/path.html](https://docs.oracle.com/javase/tutorial/essential/io/path.html) Mapping between old methods and new methods [https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping](https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping) + diff --git a/docs/readings-on-coding/javafx.md b/docs/code-howtos/javafx.md similarity index 61% rename from docs/readings-on-coding/javafx.md rename to docs/code-howtos/javafx.md index 99d83c6982b..17a30f257b1 100644 --- a/docs/readings-on-coding/javafx.md +++ b/docs/code-howtos/javafx.md @@ -1,10 +1,56 @@ --- -nav_order: 1 -parent: Readings on Coding +parent: Code Howtos +nav_order: 6 --- # Readings on JavaFX -JabRef's recommendations about JavaFX +## FXML + +The following expressions can be used in FXML attributes, according to the [official documentation](https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction\_to\_fxml.html#attributes) + +| Type | Expression | Value point to | Remark | +| -------------------------------- | ------------------------------------------------ | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| Location | `@image.png` | path relative to the current FXML file | | +| Resource | `%textToBeTranslated` | key in ResourceBundle | | +| Attribute variable | `$idOfControl` or `$variable` | named control or variable in controller (may be path in the namespace) | resolved only once at load time | +| Expression binding | `${expression}` | expression, for example `textField.text` | changes to source are propagated | +| Bidirectional expression binding | `#{expression}` | expression | changes are propagated in both directions (not yet implemented in JavaFX, see [feature request](https://bugs.openjdk.java.net/browse/JDK-8090665)) | +| Event handler | `#nameOfEventHandler` | name of the event handler method in the controller | | +| Constant | `` | constant (here `MYSTRING` in the `Strings` class) | | + +## JavaFX Radio Buttons example + +All radio buttons that should be grouped together need to have a ToggleGroup defined in the FXML code Example: + +```markup + + + + + + + + + +``` + +## JavaFX Dialogs + +All dialogs should be displayed to the user via `DialogService` interface methods. `DialogService` provides methods to display various dialogs (including custom ones) to the user. It also ensures the displayed dialog opens on the correct window via `initOwner()` (for cases where the user has multiple screens). The following code snippet demonstrates how a custom dialog is displayed to the user: + +```java +dialogService.showCustomDialog(new DocumentViewerView()); +``` + +If an instance of `DialogService` is unavailable within current class/scope in which the dialog needs to be displayed, `DialogService` can be instantiated via the code snippet shown as follows: + +```java +DialogService dialogService = Injector.instantiateModelOrService(DialogService.class); +``` ## Architecture: Model - View - (Controller) - ViewModel (MV(C)VM) diff --git a/docs/advanced-reading/jpackage.md b/docs/code-howtos/jpackage.md similarity index 95% rename from docs/advanced-reading/jpackage.md rename to docs/code-howtos/jpackage.md index e3a83411e95..8a527bcd70e 100644 --- a/docs/advanced-reading/jpackage.md +++ b/docs/code-howtos/jpackage.md @@ -1,8 +1,8 @@ --- -nav_order: 2 -parent: Advanced Reading +parent: Code Howtos +nav_order: 7 --- -# Creating a binary and debug it +# JPackage: Creating a binary and debug it JabRef uses [jpackage](https://docs.oracle.com/en/java/javase/14/jpackage/) to build binary application bundles and installers for Windows, Linux, and macOS. For Gradle, we use the [Badass JLink Plugin](https://badass-jlink-plugin.beryx.org/releases/latest/). diff --git a/docs/code-howtos/localization.md b/docs/code-howtos/localization.md new file mode 100644 index 00000000000..ed0b3567641 --- /dev/null +++ b/docs/code-howtos/localization.md @@ -0,0 +1,52 @@ +--- +parent: Code Howtos +nav_order: 8 +--- +# Localization + +More information about this topic from the translator side is provided at [Translating JabRef Interface](https://docs.jabref.org/faqcontributing/how-to-translate-the-ui). + +All labeled UI elements, descriptions and messages shown to the user should be localized, i.e., should be displayed in the chosen language. + +JabRef uses ResourceBundles ([see Oracle Tutorial](https://docs.oracle.com/javase/tutorial/i18n/resbundle/concept.html)) to store `key=value` pairs for each String to be localized. + +To show an localized String the following `org.jabref.logic.l10n.Localization` has to be used. The Class currently provides three methods to obtain translated strings: + +```java + public static String lang(String key); + + public static String lang(String key, String... params); + + public static String menuTitle(String key, String... params); +``` + +The actual usage might look like: + +```java + Localization.lang("Get me a translated String"); + Localization.lang("Using %0 or more %1 is also possible", "one", "parameter"); + Localization.menuTitle("Used for Menus only"); +``` + +General hints: + +* Use the String you want to localize directly, do not use members or local variables: `Localization.lang("Translate me");` instead of `Localization.lang(someVariable)` (possibly in the form `someVariable = Localization.lang("Translate me")` +* Use `%x`-variables where appropriate: `Localization.lang("Exported %0 entries.", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entries.");` +* Use a full stop/period (".") to end full sentences + +The tests check whether translation strings appear correctly in the resource bundles. + +1. Add new `Localization.lang("KEY")` to Java file. Run the `LocalizationConsistencyTest`under (src/test/org.jabref.logic. + + ) +2. Tests fail. In the test output a snippet is generated which must be added to the English translation file. +3. Add snippet to English translation file located at `src/main/resources/l10n/JabRef_en.properties` +4. Please do not add translations for other languages directly in the properties. They will be overwritten by [Crowdin](https://crowdin.com/project/jabref) + +## Adding a new Language + +1. Add the new Language to the Language enum in [https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/l10n/Language.java](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/l10n/Language.java) +2. Create an empty \.properties file +3. Configure the new language in [Crowdin](https://crowdin.com/project/jabref) + +If the language is a variant of a language `zh_CN` or `pt_BR` it is necessary to add a language mapping for Crowdin to the crowdin.yml file in the root. Of course the properties file also has to be named according to the language code and locale. diff --git a/docs/code-howtos/logging.md b/docs/code-howtos/logging.md new file mode 100644 index 00000000000..ca16897502e --- /dev/null +++ b/docs/code-howtos/logging.md @@ -0,0 +1,24 @@ +--- +parent: Code Howtos +nav_order: 9 +--- +## Logging + +JabRef uses the logging facade [SLF4j](https://www.slf4j.org). All log messages are passed internally to [tinylog](https://tinylog.org/v2/) which handles any filtering, formatting and writing of log messages. + +* Obtaining a logger for a class: + + ```java + private static final Logger LOGGER = LoggerFactory.getLogger(.class); + ``` +* If the logging event is caused by an exception, please add the exception to the log message as: + + ```java + catch (SomeException e) { + LOGGER.warn("Warning text.", e); + ... + } + ``` +* SLF4J also support parameterized logging, e.g. if you want to print out multiple arguments in a log statement use a pair of curly braces. [Examples](https://www.slf4j.org/faq.html#logging\_performance) +* When running tests, `tinylog-test.properties` is used. It is located under `src/test/resources`. As default, only `info` is logged. When developing, it makes sense to use `debug` as log level. One can change the log level per class using the pattern `level@class=debug` is set to `debug`. In the `.properties` file, this is done for `org.jabref.model.entry.BibEntry`. + diff --git a/docs/advanced-reading/remote-storage.md b/docs/code-howtos/remote-storage.md similarity index 96% rename from docs/advanced-reading/remote-storage.md rename to docs/code-howtos/remote-storage.md index 80f1d4d8cd1..de22f1c8501 100644 --- a/docs/advanced-reading/remote-storage.md +++ b/docs/code-howtos/remote-storage.md @@ -1,6 +1,6 @@ --- -nav_order: 5 -parent: Advanced Reading +parent: Code Howtos +nav_order: 10 --- # Remote Storage diff --git a/docs/advanced-reading/telemetry.md b/docs/code-howtos/telemetry.md similarity index 95% rename from docs/advanced-reading/telemetry.md rename to docs/code-howtos/telemetry.md index 3a8ee5525a8..33cd526a8d9 100644 --- a/docs/advanced-reading/telemetry.md +++ b/docs/code-howtos/telemetry.md @@ -1,6 +1,6 @@ --- -nav_order: 6 -parent: Advanced Reading +parent: Code Howtos +nav_order: 11 --- # Telemetry diff --git a/docs/code-howtos/testing.md b/docs/code-howtos/testing.md new file mode 100644 index 00000000000..6445a6355d1 --- /dev/null +++ b/docs/code-howtos/testing.md @@ -0,0 +1,132 @@ +--- +parent: Code Howtos +nav_order: 12 +--- +# How to test + +## Background on Java testing + +In JabRef, we mainly rely on basic JUnit tests to increase code coverage. There are other ways to test: + +| Type | Techniques | Tool (Java) | Kind of tests | Used In JabRef | +| -------------- | ------------------------------------------ | ----------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- | +| Functional | Dynamics, black box, positive and negative | [JUnit-QuickCheck](https://github.com/pholser/junit-quickcheck) | Random data generation | No, not intended, because other test kinds seem more helpful. | +| Functional | Dynamics, black box, positive and negative | [GraphWalker](https://graphwalker.github.io) | Model-based | No, because the BibDatabase doesn't need to be tests | +| Functional | Dynamics, black box, positive and negative | [TestFX](https://github.com/TestFX/TestFX) | GUI Tests | Yes | +| Functional | Dynamics, white box, negative | [PIT](https://pitest.org) | Mutation | No | +| Functional | Dynamics, white box, positive and negative | [Mockito](https://site.mockito.org) | Mocking | Yes | +| Non-functional | Dynamics, black box, positive and negative | [JETM](http://jetm.void.fm), [Apache JMeter](https://jmeter.apache.org) | Performance (performance testing vs load testing respectively) | No | +| Structural | Static, white box | [CheckStyle](https://checkstyle.sourceforge.io) | Constient formatting of the source code | Yes | +| Structural | Dynamics, white box | [SpotBugs](https://spotbugs.github.io) | Reocurreing bugs (based on experience of other projects) | No | + +## General hints on tests + +Imagine you want to test the method `format(String value)` in the class `BracesFormatter` which removes double braces in a given string. + +* _Placing:_ all tests should be placed in a class named `classTest`, e.g. `BracesFormatterTest`. +* _Naming:_ the name should be descriptive enough to describe the whole test. Use the format `methodUnderTest_ expectedBehavior_context` (without the dashes). So for example `formatRemovesDoubleBracesAtBeginning`. Try to avoid naming the tests with a `test` prefix since this information is already contained in the class name. Moreover, starting the name with `test` leads often to inferior test names (see also the [Stackoverflow discussion about naming](http://stackoverflow.com/questions/155436/unit-test-naming-best-practices)). +* _Test only one thing per test:_ tests should be short and test only one small part of the method. So instead of + + ```java + testFormat() { + assertEqual("test", format("test")); + assertEqual("{test", format("{test")); + assertEqual("test", format("test}}")); + } + ``` + + we would have five tests containing a single `assert` statement and named accordingly (`formatDoesNotChangeStringWithoutBraces`, `formatDoesNotRemoveSingleBrace`, , etc.). See [JUnit AntiPattern](https://exubero.com/junit/anti-patterns/#Multiple\_Assertions) for background. +* Do _not just test happy paths_, but also wrong/weird input. +* It is recommend to write tests _before_ you actually implement the functionality (test driven development). +* _Bug fixing:_ write a test case covering the bug and then fix it, leaving the test as a security that the bug will never reappear. +* Do not catch exceptions in tests, instead use the `assertThrows(Exception.class, ()->doSomethingThrowsEx())` feature of [junit-jupiter](https://junit.org/junit5/docs/current/user-guide/) to the test method. + +## Lists in tests + +* Use `assertEquals(Collections.emptyList(), actualList);` instead of `assertEquals(0, actualList.size());` to test whether a list is empty. +* Similarly, use `assertEquals(Arrays.asList("a", "b"), actualList);` to compare lists instead of + + ```java + assertEquals(2, actualList.size()); + assertEquals("a", actualList.get(0)); + assertEquals("b", actualList.get(1)); + ``` + +## BibEntries in tests + +* Use the `assertEquals` methods in `BibtexEntryAssert` to check that the correct BibEntry is returned. + +## Files and folders in tests + +* If you need a temporary file in tests, then add the following Annotation before the class: + + ```java + @ExtendWith(TempDirectory.class) + class TestClass{ + + @BeforeEach + void setUp(@TempDirectory.TempDir Path temporaryFolder){ + } + } + ``` + + to the test class. A temporary file is now created by `Files.createFile(path)`. Using this pattern automatically ensures that the test folder is deleted after the tests are run. See the [junit-pioneer doc](https://junit-pioneer.org/docs/temp-directory/) for more details. + +## Loading Files from Resources + +Sometimes it is necessary to load a specific resource or to access the resource directory + +```java +Path resourceDir = Paths.get(MSBibExportFormatTestFiles.class.getResource("MsBibExportFormatTest1.bib").toURI()).getParent(); +``` + +When the directory is needed, it is important to first point to an actual existing file. Otherwise the wrong directory will be returned. + +## Preferences in tests + +If you modify preference, use following pattern to ensure that the stored preferences of a developer are not affected: + +Or even better, try to mock the preferences and insert them via dependency injection. + +```java +@Test +public void getTypeReturnsBibLatexArticleInBibLatexMode() { + // Mock preferences + PreferencesService mockedPrefs = mock(PreferencesService.class); + GeneralPreferences mockedGeneralPrefs = mock(GeneralPReferences.class); + // Switch to BibLatex mode + when(mockedPrefs.getGeneralPrefs()).thenReturn(mockedGeneralPrefs); + when(mockedGeneralPrefs.getDefaultBibDatabaseMode()) + .thenReturn(BibDatabaseMode.BIBLATEX); + + // Now test + EntryTypes biblatexentrytypes = new EntryTypes(mockedPrefs); + assertEquals(BibLatexEntryTypes.ARTICLE, biblatexentrytypes.getType("article")); +} +``` + +To test that a preferences migration works successfully, use the mockito method `verify`. See `PreferencesMigrationsTest` for an example. + +## Database tests + +### PostgreSQL + +To quickly host a local PostgreSQL database, execute following statement: + +``` +docker run -d -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -p 5432:5432 --name db postgres:10 postgres -c log_statement=all +``` + +Set the environment variable `DBMS` to `postgres` (or leave it unset) + +Then, all DBMS Tests (annotated with `@org.jabref.testutils.category.DatabaseTest`) run properly. + +### MySQL + +A MySQL DBMS can be started using following command: + +``` +docker run -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=jabref -p 3800:3307 mysql:8.0 --port=3307 +``` + +Set the environment variable `DBMS` to `mysql`. diff --git a/docs/readings-on-coding/tools.md b/docs/code-howtos/tools.md similarity index 98% rename from docs/readings-on-coding/tools.md rename to docs/code-howtos/tools.md index d2a378c52b7..1bb55d88feb 100644 --- a/docs/readings-on-coding/tools.md +++ b/docs/code-howtos/tools.md @@ -1,6 +1,6 @@ --- -nav_order: 2 -parent: Readings on Coding +parent: Code Howtos +nav_order: 100 --- # Useful development tooling diff --git a/docs/advanced-reading/ui-recommendations.md b/docs/code-howtos/ui-recommendations.md similarity index 95% rename from docs/advanced-reading/ui-recommendations.md rename to docs/code-howtos/ui-recommendations.md index b2bc0c07cf4..1ae43af2e94 100644 --- a/docs/advanced-reading/ui-recommendations.md +++ b/docs/code-howtos/ui-recommendations.md @@ -1,8 +1,8 @@ --- -nav_order: 4 -parent: Advanced Reading +parent: Code Howtos +nav_order: 13 --- -# Recommendations for UI design +# UI Design Recommendations * [Designing More Efficient Forms: Structure, Inputs, Labels and Actions](https://uxplanet.org/designing-more-efficient-forms-structure-inputs-labels-and-actions-e3a47007114f) * [Input form label alignment top or left?](https://ux.stackexchange.com/questions/8480/input-form-label-alignment-top-or-left) diff --git a/docs/getting-into-the-code/code-howtos.md b/docs/getting-into-the-code/code-howtos.md deleted file mode 100644 index cee1d7a2b3d..00000000000 --- a/docs/getting-into-the-code/code-howtos.md +++ /dev/null @@ -1,431 +0,0 @@ ---- -parent: Getting into the code -nav_order: 4 ---- -# Code Howtos - -This page provides some development support in the form of howtos. See also [High Level Documentation](high-level-documentation.md). - -## Generic code how tos - -We really recommend reading the book [Java by Comparison](http://java.by-comparison.com). - -Please read [https://github.com/cxxr/better-java](https://github.com/cxxr/better-java) - -* try not to abbreviate names of variables, classes or methods -* use lowerCamelCase instead of snake\_case -* name enums in singular, e.g. `Weekday` instead of `Weekdays` (except if they represent flags) - -## Error Handling in JabRef - -### Throwing and Catching Exceptions - -Principles: - -* All exceptions we throw should be or extend `JabRefException`; This is especially important if the message stored in the Exception should be shown to the user. `JabRefException` has already implemented the `getLocalizedMessage()` method which should be used for such cases (see details below!). -* Catch and wrap all API exceptions (such as `IOExceptions`) and rethrow them - * Example: - - ```java - try { - // ... - } catch (IOException ioe) { - throw new JabRefException("Something went wrong...", - Localization.lang("Something went wrong...", ioe); - } - ``` -* Never, ever throw and catch `Exception` or `Throwable` -* Errors should only be logged when they are finally caught (i.e., logged only once). See **Logging** for details. -* If the Exception message is intended to be shown to the User in the UI (see below) provide also a localizedMessage (see `JabRefException`). - -_(Rationale and further reading:_ [https://www.baeldung.com/java-exceptions](https://www.baeldung.com/java-exceptions)_)_ - -### Outputting Errors in the UI - -Principle: Error messages shown to the User should not contain technical details (e.g., underlying exceptions, or even stack traces). Instead, the message should be concise, understandable for non-programmers and localized. The technical reasons (and stack traces) for a failure should only be logged. - -To show error message two different ways are usually used in JabRef: - -* showing an error dialog -* updating the status bar at the bottom of the main window - -``` -TODO: Usage of status bar and Swing Dialogs -``` - -## Using the EventSystem - -### What the EventSystem is used for - -Many times there is a need to provide an object on many locations simultaneously. This design pattern is quite similar to Java's Observer, but it is much simpler and readable while having the same functional sense. - -### Main principle - -`EventBus` represents a communication line between multiple components. Objects can be passed through the bus and reach the listening method of another object which is registered on that `EventBus` instance. Hence, the passed object is available as a parameter in the listening method. - -### Register to the `EventBus` - -Any listening method has to be annotated with `@Subscribe` keyword and must have only one accepting parameter. Furthermore, the object which contains such listening method(s) has to be registered using the `register(Object)` method provided by `EventBus`. The listening methods can be overloaded by using different parameter types. - -### Posting an object - -`post(object)` posts an object through the `EventBus` which has been used to register the listening/subscribing methods. - -### Short example - -```java -/* Listener.java */ - -import com.google.common.eventbus.Subscribe; - -public class Listener { - - private int value = 0; - - @Subscribe - public void listen(int value) { - this.value = value; - } - - public int getValue() { - return this.value; - } -} -``` - -```java -/* Main.java */ - -import com.google.common.eventbus.EventBus; - -public class Main { - private static EventBus eventBus = new EventBus(); - - public static void main(String[] args) { - Main main = new Main(); - Listener listener = new Listener(); - eventBus.register(listener); - eventBus.post(1); // 1 represents the passed event - - // Output should be 1 - System.out.println(listener.getValue()); - } -} -``` - -### Event handling in JabRef - -The `event` package contains some specific events which occur in JabRef. - -For example: Every time an entry was added to the database a new `EntryAddedEvent` is sent through the `eventBus` which is located in `BibDatabase`. - -If you want to catch the event you'll have to register your listener class with the `registerListener(Object listener)` method in `BibDatabase`. `EntryAddedEvent` provides also methods to get the inserted `BibEntry`. - -## Logging - -JabRef uses the logging facade [SLF4j](https://www.slf4j.org). All log messages are passed internally to [tinylog](https://tinylog.org/v2/) which handles any filtering, formatting and writing of log messages. - -* Obtaining a logger for a class: - - ```java - private static final Logger LOGGER = LoggerFactory.getLogger(.class); - ``` -* If the logging event is caused by an exception, please add the exception to the log message as: - - ```java - catch (SomeException e) { - LOGGER.warn("Warning text.", e); - ... - } - ``` -* SLF4J also support parameterized logging, e.g. if you want to print out multiple arguments in a log statement use a pair of curly braces. [Examples](https://www.slf4j.org/faq.html#logging\_performance) -* When running tests, `tinylog-test.properties` is used. It is located under `src/test/resources`. As default, only `info` is logged. When developing, it makes sense to use `debug` as log level. One can change the log level per class using the pattern `level@class=debug` is set to `debug`. In the `.properties` file, this is done for `org.jabref.model.entry.BibEntry`. - -## Using Localization correctly - -More information about this topic from the translator side is provided at [Translating JabRef Interface](https://docs.jabref.org/faqcontributing/how-to-translate-the-ui). - -All labeled UI elements, descriptions and messages shown to the user should be localized, i.e., should be displayed in the chosen language. - -JabRef uses ResourceBundles ([see Oracle Tutorial](https://docs.oracle.com/javase/tutorial/i18n/resbundle/concept.html)) to store `key=value` pairs for each String to be localized. - -To show an localized String the following `org.jabref.logic.l10n.Localization` has to be used. The Class currently provides three methods to obtain translated strings: - -```java - public static String lang(String key); - - public static String lang(String key, String... params); - - public static String menuTitle(String key, String... params); -``` - -The actual usage might look like: - -```java - Localization.lang("Get me a translated String"); - Localization.lang("Using %0 or more %1 is also possible", "one", "parameter"); - Localization.menuTitle("Used for Menus only"); -``` - -General hints: - -* Use the String you want to localize directly, do not use members or local variables: `Localization.lang("Translate me");` instead of `Localization.lang(someVariable)` (possibly in the form `someVariable = Localization.lang("Translate me")` -* Use `%x`-variables where appropriate: `Localization.lang("Exported %0 entries.", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entries.");` -* Use a full stop/period (".") to end full sentences - -The tests check whether translation strings appear correctly in the resource bundles. - -1. Add new `Localization.lang("KEY")` to Java file. Run the `LocalizationConsistencyTest`under (src/test/org.jabref.logic. - - ) -2. Tests fail. In the test output a snippet is generated which must be added to the English translation file. -3. Add snippet to English translation file located at `src/main/resources/l10n/JabRef_en.properties` -4. Please do not add translations for other languages directly in the properties. They will be overwritten by [Crowdin](https://crowdin.com/project/jabref) - -## Adding a new Language - -1. Add the new Language to the Language enum in [https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/l10n/Language.java](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/l10n/Language.java) -2. Create an empty \.properties file -3. Configure the new language in [Crowdin](https://crowdin.com/project/jabref) - -If the language is a variant of a language `zh_CN` or `pt_BR` it is necessary to add a language mapping for Crowdin to the crowdin.yml file in the root. Of course the properties file also has to be named according to the language code and locale. - -## Cleanup and Formatters - -We try to build a cleanup mechanism based on formatters. The idea is that we can register these actions in arbitrary places, e.g., onSave, onImport, onExport, cleanup, etc. and apply them to different fields. The formatters themselves are independent of any logic and therefore easy to test. - -Example: [NormalizePagesFormatter](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/formatter/bibtexfields/NormalizePagesFormatter.java) - -## Drag and Drop - -Drag and Drop makes usage of the Dragboard. For JavaFX the following [tutorial](https://docs.oracle.com/javafx/2/drag\_drop/jfxpub-drag\_drop.htm) is helpful. Note that the data has to be serializable which is put on the dragboard. For drag and drop of Bib-entries between the maintable and the groups panel, a custom Dragboard is used, `CustomLocalDragboard` which is a generic alternative to the system one. - -For accessing or putting data into the Clipboard use the `ClipboardManager`. - -## Get the JabRef frame panel - -`JabRefFrame` and `BasePanel` are the two main classes. You should never directly call them, instead pass them as parameters to the class. - -## Get Absolute Filename or Path for file in File directory - -```java -Optional file = FileHelper.expandFilename(database, fileText, preferences.getFilePreferences()); -``` - -`String path` Can be the files name or a relative path to it. The Preferences should only be directly accessed in the GUI. For the usage in logic pass them as parameter - -## Setting a Database Directory for a .bib File - -* `@comment{jabref-meta: fileDirectory:` -* “fileDirectory” is determined by Globals.pref.get(“userFileDir”) (which defaults to “fileDirectory” -* There is also “fileDirectory-\”, which is determined by Globals.prefs.get(“userFileDirIndividual”) -* Used at DatabasePropertiesDialog - -## How to work with Preferences - -`model` and `logic` must not know `JabRefPreferences`. See `ProxyPreferences` for encapsulated preferences and [https://github.com/JabRef/jabref/pull/658](https://github.com/JabRef/jabref/pull/658) for a detailed discussion. - -See [https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/preferences/TimestampPreferences.java](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/preferences/TimestampPreferences.java) (via [https://github.com/JabRef/jabref/pull/3092](https://github.com/JabRef/jabref/pull/3092)) for the current way how to deal with preferences. - -Defaults should go into the model package. See [Comments in this Commit](https://github.com/JabRef/jabref/commit/2f553e6557bddf7753b618b0f4edcaa6e873f719#commitcomment-15779484) - -## Test Cases - -### General hints on tests - -Imagine you want to test the method `format(String value)` in the class `BracesFormatter` which removes double braces in a given string. - -* _Placing:_ all tests should be placed in a class named `classTest`, e.g. `BracesFormatterTest`. -* _Naming:_ the name should be descriptive enough to describe the whole test. Use the format `methodUnderTest_ expectedBehavior_context` (without the dashes). So for example `formatRemovesDoubleBracesAtBeginning`. Try to avoid naming the tests with a `test` prefix since this information is already contained in the class name. Moreover, starting the name with `test` leads often to inferior test names (see also the [Stackoverflow discussion about naming](http://stackoverflow.com/questions/155436/unit-test-naming-best-practices)). -* _Test only one thing per test:_ tests should be short and test only one small part of the method. So instead of - - ```java - testFormat() { - assertEqual("test", format("test")); - assertEqual("{test", format("{test")); - assertEqual("test", format("test}}")); - } - ``` - - we would have five tests containing a single `assert` statement and named accordingly (`formatDoesNotChangeStringWithoutBraces`, `formatDoesNotRemoveSingleBrace`, , etc.). See [JUnit AntiPattern](https://exubero.com/junit/anti-patterns/#Multiple\_Assertions) for background. -* Do _not just test happy paths_, but also wrong/weird input. -* It is recommend to write tests _before_ you actually implement the functionality (test driven development). -* _Bug fixing:_ write a test case covering the bug and then fix it, leaving the test as a security that the bug will never reappear. -* Do not catch exceptions in tests, instead use the `assertThrows(Exception.class, ()->doSomethingThrowsEx())` feature of [junit-jupiter](https://junit.org/junit5/docs/current/user-guide/) to the test method. - -### Lists in tests - -* Use `assertEquals(Collections.emptyList(), actualList);` instead of `assertEquals(0, actualList.size());` to test whether a list is empty. -* Similarly, use `assertEquals(Arrays.asList("a", "b"), actualList);` to compare lists instead of - - ```java - assertEquals(2, actualList.size()); - assertEquals("a", actualList.get(0)); - assertEquals("b", actualList.get(1)); - ``` - -### BibEntries in tests - -* Use the `assertEquals` methods in `BibtexEntryAssert` to check that the correct BibEntry is returned. - -### Files and folders in tests - -* If you need a temporary file in tests, then add the following Annotation before the class: - - ```java - @ExtendWith(TempDirectory.class) - class TestClass{ - - @BeforeEach - void setUp(@TempDirectory.TempDir Path temporaryFolder){ - } - } - ``` - - to the test class. A temporary file is now created by `Files.createFile(path)`. Using this pattern automatically ensures that the test folder is deleted after the tests are run. See the [junit-pioneer doc](https://junit-pioneer.org/docs/temp-directory/) for more details. - -### Loading Files from Resources - -Sometimes it is necessary to load a specific resource or to access the resource directory - -```java -Path resourceDir = Paths.get(MSBibExportFormatTestFiles.class.getResource("MsBibExportFormatTest1.bib").toURI()).getParent(); -``` - -When the directory is needed, it is important to first point to an actual existing file. Otherwise the wrong directory will be returned. - -### Preferences in tests - -If you modify preference, use following pattern to ensure that the stored preferences of a developer are not affected: - -Or even better, try to mock the preferences and insert them via dependency injection. - -```java -@Test -public void getTypeReturnsBibLatexArticleInBibLatexMode() { - // Mock preferences - PreferencesService mockedPrefs = mock(PreferencesService.class); - GeneralPreferences mockedGeneralPrefs = mock(GeneralPReferences.class); - // Switch to BibLatex mode - when(mockedPrefs.getGeneralPrefs()).thenReturn(mockedGeneralPrefs); - when(mockedGeneralPrefs.getDefaultBibDatabaseMode()) - .thenReturn(BibDatabaseMode.BIBLATEX); - - // Now test - EntryTypes biblatexentrytypes = new EntryTypes(mockedPrefs); - assertEquals(BibLatexEntryTypes.ARTICLE, biblatexentrytypes.getType("article")); -} -``` - -To test that a preferences migration works successfully, use the mockito method `verify`. See `PreferencesMigrationsTest` for an example. - -### Background on Java testing - -In JabRef, we mainly rely to basic JUnit tests to increase code coverage. There are other ways to test: - -| Type | Techniques | Tool (Java) | Kind of tests | Used In JabRef | -| -------------- | ------------------------------------------ | ----------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- | -| Functional | Dynamics, black box, positive and negative | [JUnit-QuickCheck](https://github.com/pholser/junit-quickcheck) | Random data generation | No, not intended, because other test kinds seem more helpful. | -| Functional | Dynamics, black box, positive and negative | [GraphWalker](https://graphwalker.github.io) | Model-based | No, because the BibDatabase doesn't need to be tests | -| Functional | Dynamics, black box, positive and negative | [TestFX](https://github.com/TestFX/TestFX) | GUI Tests | Yes | -| Functional | Dynamics, white box, negative | [PIT](https://pitest.org) | Mutation | No | -| Functional | Dynamics, white box, positive and negative | [Mockito](https://site.mockito.org) | Mocking | Yes | -| Non-functional | Dynamics, black box, positive and negative | [JETM](http://jetm.void.fm), [Apache JMeter](https://jmeter.apache.org) | Performance (performance testing vs load testing respectively) | No | -| Structural | Static, white box | [CheckStyle](https://checkstyle.sourceforge.io) | Constient formatting of the source code | Yes | -| Structural | Dynamics, white box | [SpotBugs](https://spotbugs.github.io) | Reocurreing bugs (based on experience of other projects) | No | - -## UI - -Global variables should be avoided. Try to pass them as dependency. - -## "Special Fields" - -### keywords sync - -Database.addDatabaseChangeListener does not work as the DatabaseChangedEvent does not provide the field information. Therefore, we have to use BibtexEntry.addPropertyChangeListener(VetoableChangeListener listener) - -## Working with BibTeX data - -### Working with authors - -You can normalize the authors using `org.jabref.model.entry.AuthorList.fixAuthor_firstNameFirst(String)`. Then the authors always look nice. The only alternative containing all data of the names is `org.jabref.model.entry.AuthorList.fixAuthor_lastNameFirst(String)`. The other `fix...` methods omit data (like the von parts or the junior information). - -## Benchmarks - -* Benchmarks can be executed by running the `jmh` gradle task (this functionality uses the [JMH Gradle plugin](https://github.com/melix/jmh-gradle-plugin)) -* Best practices: - * Read test input from `@State` objects - * Return result of calculations (either explicitly or via a `BlackHole` object) -* [List of examples](https://github.com/melix/jmh-gradle-example/tree/master/src/jmh/java/org/openjdk/jmh/samples) - -## Measure performance - -Try out the [YourKit JAva Profiler](https://www.yourkit.com). - -## equals - -When creating an `equals` method follow: - -1. Use the `==` operator to check if the argument is a reference to this object. If so, return `true`. -2. Use the `instanceof` operator to check if the argument has the correct type. If not, return `false`. -3. Cast the argument to the correct type. -4. For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object. If all these tests succeed, return `true` otherwise, return `false`. -5. When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent? - -Also, note: - -* Always override `hashCode` when you override equals (`hashCode` also has very strict rules) (Item 9 of[Effective Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/)) -* Don’t try to be too clever -* Don’t substitute another type for `Object` in the equals declaration - -## Files and Paths - -Always try to use the methods from the nio-package. For interoperability, they provide methods to convert between file and path. [https://docs.oracle.com/javase/tutorial/essential/io/path.html](https://docs.oracle.com/javase/tutorial/essential/io/path.html) Mapping between old methods and new methods [https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping](https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping) - -## JavaFX - -The following expressions can be used in FXML attributes, according to the [official documentation](https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction\_to\_fxml.html#attributes) - -| Type | Expression | Value point to | Remark | -| -------------------------------- | ------------------------------------------------ | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -| Location | `@image.png` | path relative to the current FXML file | | -| Resource | `%textToBeTranslated` | key in ResourceBundle | | -| Attribute variable | `$idOfControl` or `$variable` | named control or variable in controller (may be path in the namespace) | resolved only once at load time | -| Expression binding | `${expression}` | expression, for example `textField.text` | changes to source are propagated | -| Bidirectional expression binding | `#{expression}` | expression | changes are propagated in both directions (not yet implemented in JavaFX, see [feature request](https://bugs.openjdk.java.net/browse/JDK-8090665)) | -| Event handler | `#nameOfEventHandler` | name of the event handler method in the controller | | -| Constant | `` | constant (here `MYSTRING` in the `Strings` class) | | - -### JavaFX Radio Buttons example - -All radio buttons that should be grouped together need to have a ToggleGroup defined in the FXML code Example: - -```markup - - - - - - - - - -``` - -### JavaFX Dialogs - -All dialogs should be displayed to the user via `DialogService` interface methods. `DialogService` provides methods to display various dialogs (including custom ones) to the user. It also ensures the displayed dialog opens on the correct window via `initOwner()` (for cases where the user has multiple screens). The following code snippet demonstrates how a custom dialog is displayed to the user: - -```java -dialogService.showCustomDialog(new DocumentViewerView()); -``` - -If an instance of `DialogService` is unavailable within current class/scope in which the dialog needs to be displayed, `DialogService` can be instantiated via the code snippet shown as follows: - -```java -DialogService dialogService = Injector.instantiateModelOrService(DialogService.class); -``` diff --git a/docs/getting-into-the-code/development-strategy.md b/docs/getting-into-the-code/development-strategy.md index d9ba466b484..84a6fea798b 100644 --- a/docs/getting-into-the-code/development-strategy.md +++ b/docs/getting-into-the-code/development-strategy.md @@ -1,6 +1,6 @@ --- parent: Getting into the code -nav_order: 5 +nav_order: 1 --- # JabRef's development strategy @@ -14,7 +14,7 @@ To ensure high code-quality, * We document our design decisions using the lightweight architectural decision records [MADR](https://adr.github.io/madr/). * We review each external pull request by at least two [JabRef Core Developers](https://github.com/JabRef/jabref/blob/main/MAINTAINERS/README.md). -Read on about our automated quality checks at [Code Quality](../advanced-reading/code-quality.md). +Read on about our automated quality checks at [Code Quality](../code-howtos/code-quality.md). ## Continuous integration diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md index dd8ff6ee03c..409416b9e9b 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md @@ -1,6 +1,6 @@ --- parent: Getting into the code -nav_order: 1 +nav_order: 2 --- # Set up a local workspace @@ -70,7 +70,7 @@ Eclipse JEE 2022-03 or newer is required. ### Other Tooling -See our [tool recommendations](../readings-on-coding/tools.md). +See our [tool recommendations](../code-howtos/tools.md). ## Get the code diff --git a/docs/getting-into-the-code/high-level-documentation.md b/docs/getting-into-the-code/high-level-documentation.md index 4539b9c33d8..315246f062d 100644 --- a/docs/getting-into-the-code/high-level-documentation.md +++ b/docs/getting-into-the-code/high-level-documentation.md @@ -1,6 +1,6 @@ --- parent: Getting into the code -nav_order: 2 +nav_order: 3 --- # High-level documentation diff --git a/docs/getting-into-the-code/testing.md b/docs/getting-into-the-code/testing.md deleted file mode 100644 index 488f77a4d7a..00000000000 --- a/docs/getting-into-the-code/testing.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -parent: Getting into the code -nav_order: 3 ---- -# How to test - -For details on unit testing see [https://devdocs.jabref.org/getting-into-the-code/code-howtos#test-cases](https://devdocs.jabref.org/getting-into-the-code/code-howtos#test-cases). - -## Database tests - -### PostgreSQL - -To quickly host a local PostgreSQL database, execute following statement: - -``` -docker run -d -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -p 5432:5432 --name db postgres:10 postgres -c log_statement=all -``` - -Set the environment variable `DBMS` to `postgres` (or leave it unset) - -Then, all DBMS Tests (annotated with `@org.jabref.testutils.category.DatabaseTest`) run properly. - -### MySQL - -A MySQL DBMS can be started using following command: - -``` -docker run -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=jabref -p 3800:3307 mysql:8.0 --port=3307 -``` - -Set the environment variable `DBMS` to `mysql`. diff --git a/docs/readings-on-coding/index.md b/docs/readings-on-coding/index.md deleted file mode 100644 index b612d0df6f3..00000000000 --- a/docs/readings-on-coding/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -nav_order: 6 -has_children: true ---- -# Readings on Coding From b8bb186a8bca48081a81c09a127ec9803d4e0b8c Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 14 Aug 2022 22:34:04 +0200 Subject: [PATCH 2/5] Restructure top elements --- docs/decisions/index.md | 2 +- docs/getting-into-the-code/index.md | 2 +- docs/teaching.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/decisions/index.md b/docs/decisions/index.md index f6f60378dd3..bab7c46aec3 100644 --- a/docs/decisions/index.md +++ b/docs/decisions/index.md @@ -1,5 +1,5 @@ --- -nav_order: 3 +nav_order: 4 has_children: true --- # Decision Records diff --git a/docs/getting-into-the-code/index.md b/docs/getting-into-the-code/index.md index b6e28461c59..7104747e918 100644 --- a/docs/getting-into-the-code/index.md +++ b/docs/getting-into-the-code/index.md @@ -1,5 +1,5 @@ --- -nav_order: 4 +nav_order: 5 has_children: true --- # Getting into the code diff --git a/docs/teaching.md b/docs/teaching.md index fdf628d7073..097110ce58e 100644 --- a/docs/teaching.md +++ b/docs/teaching.md @@ -1,5 +1,5 @@ --- -nav_order: 6 +nav_order: 3 --- # JabRef and Software Engineering Training From e3241428b6a24c1b19a7e2dfe8e5fba79456d62f Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 14 Aug 2022 22:40:50 +0200 Subject: [PATCH 3/5] Make titles more alphabetical --- docs/code-howtos/eventbus.md | 2 +- docs/code-howtos/fetchers.md | 2 +- docs/code-howtos/javafx.md | 2 +- docs/code-howtos/testing.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/code-howtos/eventbus.md b/docs/code-howtos/eventbus.md index efb862335d1..5d90d47b22c 100644 --- a/docs/code-howtos/eventbus.md +++ b/docs/code-howtos/eventbus.md @@ -2,7 +2,7 @@ parent: Code Howtos nav_order: 4 --- -# Using the EventSystem +# Event Bus and Event System ## What the EventSystem is used for diff --git a/docs/code-howtos/fetchers.md b/docs/code-howtos/fetchers.md index 446151eff21..5c4edf7225f 100644 --- a/docs/code-howtos/fetchers.md +++ b/docs/code-howtos/fetchers.md @@ -2,7 +2,7 @@ parent: Code Howtos nav_order: 5 --- -# Working on fetchers +# Fetchers Fetchers are the implementation of the [search using online services](https://docs.jabref.org/collect/import-using-online-bibliographic-database). Some fetchers require API keys to get them working. To get the fetchers running in a JabRef development setup, the keys need to be placed in the respective environment variable. The following table lists the respective fetchers, where to get the key from and the environment variable where the key has to be placed. diff --git a/docs/code-howtos/javafx.md b/docs/code-howtos/javafx.md index 17a30f257b1..b67e04cafea 100644 --- a/docs/code-howtos/javafx.md +++ b/docs/code-howtos/javafx.md @@ -2,7 +2,7 @@ parent: Code Howtos nav_order: 6 --- -# Readings on JavaFX +# JavaFX ## FXML diff --git a/docs/code-howtos/testing.md b/docs/code-howtos/testing.md index 6445a6355d1..439d384d799 100644 --- a/docs/code-howtos/testing.md +++ b/docs/code-howtos/testing.md @@ -2,7 +2,7 @@ parent: Code Howtos nav_order: 12 --- -# How to test +# Testing JabRef ## Background on Java testing From d4a218229ae57b8ec0cb64b1a4f29007097f343d Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 15 Aug 2022 21:30:15 +0200 Subject: [PATCH 4/5] WIP: Move open office docs to sub folder --- docs/{ => code-howtos}/openoffice/code-reorganization.md | 0 docs/{ => code-howtos}/openoffice/index.md | 3 ++- docs/{ => code-howtos}/openoffice/layers-v1.svg | 0 docs/{ => code-howtos}/openoffice/ooresult-ooerror/index.md | 0 .../openoffice/ooresult-ooerror/ooresult-alternatives.md | 0 docs/{ => code-howtos}/openoffice/order-of-appearance.md | 0 docs/{ => code-howtos}/openoffice/overview.md | 0 docs/{ => code-howtos}/openoffice/problems.md | 0 docs/code-howtos/remote-storage.md | 2 +- docs/code-howtos/telemetry.md | 2 +- docs/code-howtos/testing.md | 2 +- docs/code-howtos/ui-recommendations.md | 2 +- 12 files changed, 6 insertions(+), 5 deletions(-) rename docs/{ => code-howtos}/openoffice/code-reorganization.md (100%) rename docs/{ => code-howtos}/openoffice/index.md (60%) rename docs/{ => code-howtos}/openoffice/layers-v1.svg (100%) rename docs/{ => code-howtos}/openoffice/ooresult-ooerror/index.md (100%) rename docs/{ => code-howtos}/openoffice/ooresult-ooerror/ooresult-alternatives.md (100%) rename docs/{ => code-howtos}/openoffice/order-of-appearance.md (100%) rename docs/{ => code-howtos}/openoffice/overview.md (100%) rename docs/{ => code-howtos}/openoffice/problems.md (100%) diff --git a/docs/openoffice/code-reorganization.md b/docs/code-howtos/openoffice/code-reorganization.md similarity index 100% rename from docs/openoffice/code-reorganization.md rename to docs/code-howtos/openoffice/code-reorganization.md diff --git a/docs/openoffice/index.md b/docs/code-howtos/openoffice/index.md similarity index 60% rename from docs/openoffice/index.md rename to docs/code-howtos/openoffice/index.md index 2fbb9dab491..847375d6616 100644 --- a/docs/openoffice/index.md +++ b/docs/code-howtos/openoffice/index.md @@ -1,5 +1,6 @@ --- -nav_order: 7 +parent: Code Howtos +nav_order: 10 has_children: true --- # The LibreOffice Panel diff --git a/docs/openoffice/layers-v1.svg b/docs/code-howtos/openoffice/layers-v1.svg similarity index 100% rename from docs/openoffice/layers-v1.svg rename to docs/code-howtos/openoffice/layers-v1.svg diff --git a/docs/openoffice/ooresult-ooerror/index.md b/docs/code-howtos/openoffice/ooresult-ooerror/index.md similarity index 100% rename from docs/openoffice/ooresult-ooerror/index.md rename to docs/code-howtos/openoffice/ooresult-ooerror/index.md diff --git a/docs/openoffice/ooresult-ooerror/ooresult-alternatives.md b/docs/code-howtos/openoffice/ooresult-ooerror/ooresult-alternatives.md similarity index 100% rename from docs/openoffice/ooresult-ooerror/ooresult-alternatives.md rename to docs/code-howtos/openoffice/ooresult-ooerror/ooresult-alternatives.md diff --git a/docs/openoffice/order-of-appearance.md b/docs/code-howtos/openoffice/order-of-appearance.md similarity index 100% rename from docs/openoffice/order-of-appearance.md rename to docs/code-howtos/openoffice/order-of-appearance.md diff --git a/docs/openoffice/overview.md b/docs/code-howtos/openoffice/overview.md similarity index 100% rename from docs/openoffice/overview.md rename to docs/code-howtos/openoffice/overview.md diff --git a/docs/openoffice/problems.md b/docs/code-howtos/openoffice/problems.md similarity index 100% rename from docs/openoffice/problems.md rename to docs/code-howtos/openoffice/problems.md diff --git a/docs/code-howtos/remote-storage.md b/docs/code-howtos/remote-storage.md index de22f1c8501..2f01a096452 100644 --- a/docs/code-howtos/remote-storage.md +++ b/docs/code-howtos/remote-storage.md @@ -1,6 +1,6 @@ --- parent: Code Howtos -nav_order: 10 +nav_order: 11 --- # Remote Storage diff --git a/docs/code-howtos/telemetry.md b/docs/code-howtos/telemetry.md index 33cd526a8d9..6393c4179c6 100644 --- a/docs/code-howtos/telemetry.md +++ b/docs/code-howtos/telemetry.md @@ -1,6 +1,6 @@ --- parent: Code Howtos -nav_order: 11 +nav_order: 12 --- # Telemetry diff --git a/docs/code-howtos/testing.md b/docs/code-howtos/testing.md index 439d384d799..f8de3c35c37 100644 --- a/docs/code-howtos/testing.md +++ b/docs/code-howtos/testing.md @@ -1,6 +1,6 @@ --- parent: Code Howtos -nav_order: 12 +nav_order: 13 --- # Testing JabRef diff --git a/docs/code-howtos/ui-recommendations.md b/docs/code-howtos/ui-recommendations.md index 1ae43af2e94..cddd56f8c37 100644 --- a/docs/code-howtos/ui-recommendations.md +++ b/docs/code-howtos/ui-recommendations.md @@ -1,6 +1,6 @@ --- parent: Code Howtos -nav_order: 13 +nav_order: 14 --- # UI Design Recommendations From e9298f8af617b72e90962a8e9adf1def37afe833 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 15 Aug 2022 23:10:29 +0200 Subject: [PATCH 5/5] Fix OO doc --- docs/code-howtos/openoffice/code-reorganization.md | 1 + docs/code-howtos/openoffice/index.md | 2 +- docs/code-howtos/openoffice/order-of-appearance.md | 1 + docs/code-howtos/openoffice/overview.md | 1 + docs/code-howtos/openoffice/problems.md | 1 + docs/code-howtos/telemetry.md | 2 +- docs/code-howtos/testing.md | 2 +- docs/code-howtos/ui-recommendations.md | 2 +- 8 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/code-howtos/openoffice/code-reorganization.md b/docs/code-howtos/openoffice/code-reorganization.md index 9b9999b4e43..c9c37fe47d8 100644 --- a/docs/code-howtos/openoffice/code-reorganization.md +++ b/docs/code-howtos/openoffice/code-reorganization.md @@ -1,6 +1,7 @@ --- nav_order: 4 parent: The LibreOffice Panel +grand_parent: Code Howtos --- # Code reorganization diff --git a/docs/code-howtos/openoffice/index.md b/docs/code-howtos/openoffice/index.md index 847375d6616..a36a86fd2b5 100644 --- a/docs/code-howtos/openoffice/index.md +++ b/docs/code-howtos/openoffice/index.md @@ -1,6 +1,6 @@ --- parent: Code Howtos -nav_order: 10 +nav_order: 12 has_children: true --- # The LibreOffice Panel diff --git a/docs/code-howtos/openoffice/order-of-appearance.md b/docs/code-howtos/openoffice/order-of-appearance.md index fa2b529f374..2848b89c0e5 100644 --- a/docs/code-howtos/openoffice/order-of-appearance.md +++ b/docs/code-howtos/openoffice/order-of-appearance.md @@ -1,6 +1,7 @@ --- nav_order: 2 parent: The LibreOffice Panel +grand_parent: Code Howtos --- # Order of appearance of citation groups diff --git a/docs/code-howtos/openoffice/overview.md b/docs/code-howtos/openoffice/overview.md index 315c7a62989..56ec4d857ce 100644 --- a/docs/code-howtos/openoffice/overview.md +++ b/docs/code-howtos/openoffice/overview.md @@ -1,6 +1,7 @@ --- nav_order: 1 parent: The LibreOffice Panel +grand_parent: Code Howtos --- # Overview diff --git a/docs/code-howtos/openoffice/problems.md b/docs/code-howtos/openoffice/problems.md index 12cbbcb9957..4d11dc62966 100644 --- a/docs/code-howtos/openoffice/problems.md +++ b/docs/code-howtos/openoffice/problems.md @@ -1,6 +1,7 @@ --- nav_order: 3 parent: The LibreOffice Panel +grand_parent: Code Howtos --- # Problems diff --git a/docs/code-howtos/telemetry.md b/docs/code-howtos/telemetry.md index 6393c4179c6..d070ee2d813 100644 --- a/docs/code-howtos/telemetry.md +++ b/docs/code-howtos/telemetry.md @@ -1,6 +1,6 @@ --- parent: Code Howtos -nav_order: 12 +nav_order: 13 --- # Telemetry diff --git a/docs/code-howtos/testing.md b/docs/code-howtos/testing.md index f8de3c35c37..fe3493f6a3d 100644 --- a/docs/code-howtos/testing.md +++ b/docs/code-howtos/testing.md @@ -1,6 +1,6 @@ --- parent: Code Howtos -nav_order: 13 +nav_order: 14 --- # Testing JabRef diff --git a/docs/code-howtos/ui-recommendations.md b/docs/code-howtos/ui-recommendations.md index cddd56f8c37..e81ea116f4a 100644 --- a/docs/code-howtos/ui-recommendations.md +++ b/docs/code-howtos/ui-recommendations.md @@ -1,6 +1,6 @@ --- parent: Code Howtos -nav_order: 14 +nav_order: 15 --- # UI Design Recommendations