2025-01-11 12:30:11 +01:00

128 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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-the-code/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>.
* 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)
## Dependency injection
JabRef uses a [fork](https://github.com/JabRef/afterburner.fx) of the [afterburner.fx framework](https://github.com/AdamBien/afterburner.fx) by [Adam Bien](https://adam-bien.com/).
The main idea is to get instances by using `Injector.instantiateModelOrService(X.class)`, where `X` is the instance one needs.
The method `instantiateModelOrService` checks if there is already an instance of the given class. If yes, it returns it. If not, it creates a new one.
A singleton can be added by `com.airhacks.afterburner.injection.Injector#setModelOrService(X.class, y)`, where X is the class and y the instance you want to inject.
## 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
JabRef stores files relative to one of [multiple possible directories](https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#directories-for-files).
The convert the relative path to an absolute one, there is the `find` method in `FileUtil`:
```java
org.jabref.logic.util.io.FileUtil.find(org.jabref.model.database.BibDatabaseContext, java.lang.String, org.jabref.preferences.FilePreferences)
```
`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
## Get a relative filename (or path) for a file
[JabRef offers multiple directories per library to store a file.](https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#directories-for-files).
When adding a file to a library, the path should be stored relative to "the best matching" directory of these.
This is implemented in `FileUtil`:
```java
org.jabref.logic.util.io.FileUtil.relativize(java.nio.file.Path, org.jabref.model.database.BibDatabaseContext, org.jabref.preferences.FilePreferences)
```
## Setting a Directory for a .bib File
* `@comment{jabref-meta: fileDirectory:<directory>`
* “fileDirectory” is determined by Globals.pref.get(“userFileDir”) (which defaults to “fileDirectory”
* There is also “fileDirectory-\<username>”, 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/))
* Dont try to be too clever
* Dont 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)