Updated jabref to hash 6af91b9

This commit is contained in:
0nlineSam 2025-01-11 12:30:11 +01:00
parent b157c2c194
commit a2a1ede6b7
1022 changed files with 13816 additions and 209582 deletions
jabref
.devcontainer
.github
.gitpod.Dockerfile
.vscode
CHANGELOG.mdCONTRIBUTING.mdPRIVACY.mdbuild.gradle
buildres
config
docs
external-libraries.md
gradle/wrapper
nix
rewrite.yml
scripts/vms
shell.nix
snap
src

@ -31,9 +31,9 @@
// Install java.
// See https://github.com/devcontainers/features/tree/main/src/java#options for details.
"ghcr.io/devcontainers/features/java:1": {
"version": "23.0.1-tem",
"version": "21.0.1-librca",
"installGradle": false,
"jdkDistro": "Temurin"
"jdkDistro": "librca"
}
}
}

@ -1,5 +1,10 @@
Describe the changes you have made here: what, why, ...
Link the issue that will be closed, e.g., "Closes #333". If your PR closes a koppor issue, link it using its URL, e.g., "Closes https://github.com/koppor/jabref/issues/47".
<!--
Describe the changes you have made here: what, why, ...
Link the issue that will be closed, e.g., "Closes #333".
If your PR closes a koppor issue, link it using its URL, e.g., "Closes https://github.com/koppor/jabref/issues/47".
"Closes" is a keyword GitHub uses to link PRs with issues; do not change it.
Don't reference an issue in the PR title because GitHub does not support auto-linking there.
-->
### Mandatory checks
@ -8,8 +13,7 @@ Link the issue that will be closed, e.g., "Closes #333". If your PR closes a kop
- [x] done; [ ] not done / not applicable
-->
- [x] I own the copyright of the code submitted and I licence it under the [MIT license](https://github.com/JabRef/jabref/blob/main/LICENSE)
- [ ] Change in `CHANGELOG.md` described in a way that is understandable for the average user (if change is visible to the user)
- [ ] Change in `CHANGELOG.md` described in a way that is understandable for the average user (if applicable)
- [ ] Tests created for changes (if applicable)
- [ ] Manually tested changed features in running JabRef (always required)
- [ ] Screenshots added in PR description (for UI changes)

@ -2,8 +2,12 @@
message: |
Your code currently does not meet [JabRef's code guidelines](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-13-code-style.html).
We use [Checkstyle](https://checkstyle.sourceforge.io/) to identify issues.
The tool reviewdog already placed comments on GitHub to indicate the places. See the tab "Files" in you PR.
Please carefully follow [the setup guide for the codestyle](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-13-code-style.html).
Afterwards, please [run checkstyle locally](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-13-code-style.html#run-checkstyle) and fix the issues.
You can check review dog's comments at the tab "Files changed" of your pull request.
- jobName: OpenRewrite
message: |
Your code currently does not meet JabRef's code guidelines.
@ -30,8 +34,3 @@
message: |
While the PR was in progress, a new version of JabRef has been released.
You have to merge `upstream/main` and move your entry in `CHANGELOG.md` up to the section `## [Unreleased]`.
- jobName: 'Unit tests'
message: |
JUnit tests are failing. In the area "Some checks were not successful", locate "Tests / Unit tests (pull_request)" and click on "Details". This brings you to the test output.
You can then run these tests in IntelliJ to reproduce the failing tests locally. We offer a quick test running howto in the section [Final build system checks](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.html#final-build-system-checks) in our setup guide.

@ -0,0 +1,31 @@
name: Add greeting to issues for first time contributors
on:
issues:
types:
- labeled
pull_request_target:
types:
- labeled
jobs:
GreetingFirstTimeCodeContribution:
if: ${{ github.event.label.name == 'FirstTimeCodeContribution' }}
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: GreetingFirstTimeCodeContribution
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.issue.number || github.event.pull_request.number }}
body: |
Welcome to the vibrant world of open-source development with JabRef!
Newcomers, we're excited to have you on board. Start by exploring our [Contributing](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md) guidelines, and don't forget to check out our [workspace setup guidelines](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace) to get started smoothly.
In case you encounter failing tests during development, please check our [developer FAQs](https://devdocs.jabref.org/code-howtos/faq.html)!
Having any questions or issues? Feel free to ask here on GitHub. Need help setting up your local workspace? Join the conversation on [JabRef's Gitter chat](https://gitter.im/JabRef/jabref). And don't hesitate to open a (draft) pull request early on to show the direction it is heading towards. This way, you will receive valuable feedback.
Happy coding! 🚀

@ -0,0 +1,44 @@
name: Add to Project on Label
on:
issues:
types: [labeled]
permissions:
issues: write
jobs:
add-to-project:
runs-on: ubuntu-latest
steps:
- name: "good first issue"
if: "${{ github.event.label.name == 'good first issue' }}"
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ITEM_ADD }}
run: |
ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH")
gh project item-add 5 --owner JabRef --url $ISSUE_URL
- name: needs-refinement
if: github.event.label.name == 'needs-refinement'
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ITEM_ADD }}
run: |
ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH")
gh project item-add 15 --owner JabRef --url $ISSUE_URL
- name: "status: freeze"
if: "${{ github.event.label.name == 'status: freeze' }}"
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ITEM_ADD }}
run: |
ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH")
gh project item-add 9 --owner JabRef --url $ISSUE_URL
- name: ui
if: "${{ github.event.label.name == 'ui' }}"
env:
GH_DEBUG: api
GH_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ITEM_ADD }}
run: |
ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH")
echo $ISSUE_URL
gh project item-add 8 --owner JabRef --url $ISSUE_URL

@ -1,57 +0,0 @@
name: Assign Issue
on:
schedule:
- cron: 4 12 * * *
issue_comment:
types: [created]
workflow_dispatch:
jobs:
assign:
if: github.repository_owner == 'JabRef'
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Assign the user or unassign stale assignments
id: assign
uses: takanome-dev/assign-issue-action@beta
with:
github_token: '${{ secrets.GITHUB_TOKEN }}'
days_until_unassign: 90
maintainers: koppor, Siedlerchr, ThiloteE, calixtus, HoussemNasri
assigned_comment: |
👋 Hey @{{ handle }}, thank you for your interest in this issue! 🎉
We're excited to have you on board. Start by exploring our [Contributing](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md) guidelines, and don't forget to check out our [workspace setup guidelines](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace) to get started smoothly.
In case you encounter failing tests during development, please check our [developer FAQs](https://devdocs.jabref.org/code-howtos/faq.html)!
Having any questions or issues? Feel free to ask here on GitHub. Need help setting up your local workspace? Join the conversation on [JabRef's Gitter chat](https://gitter.im/JabRef/jabref). And don't hesitate to open a (draft) pull request early on to show the direction it is heading towards. This way, you will receive valuable feedback.
Happy coding! 🚀
⏳ Please note, you will be automatically unassigned if the issue isn't closed within **{{ total_days }} days** (by **{{ unassigned_date }}**). A maintainer can also add the "**{{ pin_label }}**"" label to prevent automatic unassignment.
- name: Move Issue to "Assigned" Column in "Candidates for University Projects"
if: steps.assign.outputs.assigned == 'yes'
uses: m7kvqbe1/github-action-move-issues@feat/skip-if-not-in-project-flag
with:
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
project-url: "https://github.com/orgs/JabRef/projects/3"
target-labels: "📍 Assigned"
target-column: "Assigned"
ignored-columns: ""
default-column: "Free to take"
skip-if-not-in-project: true
- name: Move Issue to "Assigned" Column in "Good First Issues"
if: steps.assign.outputs.assigned == 'yes'
uses: m7kvqbe1/github-action-move-issues@feat/skip-if-not-in-project-flag
with:
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
project-url: "https://github.com/orgs/JabRef/projects/5"
target-labels: "📍 Assigned"
target-column: "Assigned"
ignored-columns: ""
default-column: "Free to take"
skip-if-not-in-project: true

@ -10,13 +10,13 @@ jobs:
runs-on: ubuntu-latest
# Run only if PR is inside JabRef's main repository and created by dependabot or by an update workflow
if: >
(github.repository == 'JabRef/jabref') &&
(github.repository == 'JabRef/jabref') &&
(github.event.pull_request.head.repo.full_name == 'JabRef/jabref') &&
(
(github.actor == 'dependabot[bot]') ||
(
startsWith(github.event.pull_request.title, '[Bot] ') ||
startsWith(github.event.pull_request.title, 'Bump ') ||
startsWith(github.event.pull_request.title, '[Bot] ') ||
startsWith(github.event.pull_request.title, 'Bump ') ||
startsWith(github.event.pull_request.title, 'New Crowdin updates') ||
startsWith(github.event.pull_request.title, 'Update Gradle Wrapper from')
)
@ -26,9 +26,9 @@ jobs:
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GH_TOKEN: ${{secrets.GH_TOKEN_JABREF_MACHINE_PR_APPROVE}}
GITHUB_TOKEN: ${{secrets.GH_TOKEN_JABREF_MACHINE_PR_APPROVE}}
- name: Merge PR
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GH_TOKEN: ${{secrets.GH_TOKEN_UPDATE_GRADLE_WRAPPER}}
GITHUB_TOKEN: ${{secrets.GH_TOKEN_UPDATE_GRADLE_WRAPPER}}

@ -18,7 +18,6 @@ concurrency:
jobs:
lychee:
if: github.repository_owner == 'JabRef'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -32,7 +31,7 @@ jobs:
restore-keys: cache-lychee-
- name: Link Checker
id: lychee
uses: lycheeverse/lychee-action@v2.1.0
uses: lycheeverse/lychee-action@v1.10.0
with:
fail: true
args: --accept '200,201,202,203,204,403,429,500' --max-concurrency 1 --cache --no-progress --exclude-all-private './**/*.md'

@ -6,7 +6,6 @@ on:
jobs:
cleanup:
if: github.repository_owner == 'JabRef'
runs-on: ubuntu-latest
steps:
- name: Cancel deployment run
@ -29,7 +28,7 @@ jobs:
BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }}
- name: Delete folder on builds.jabref.org
if: steps.checksecrets.outputs.secretspresent == 'YES'
uses: appleboy/ssh-action@v1.2.0
uses: appleboy/ssh-action@v1.0.3
with:
script: rm -rf /var/www/builds.jabref.org/www/pull/${{ github.event.pull_request.number }} || true
host: build-upload.jabref.org
@ -38,8 +37,8 @@ jobs:
key: ${{ secrets.buildJabRefPrivateKey }}
- name: Update PR comment
if: steps.checksecrets.outputs.secretspresent == 'YES'
uses: thollander/actions-comment-pull-request@v3
uses: thollander/actions-comment-pull-request@v2
with:
comment-tag: download-link
comment_tag: download-link
message: The build for this PR is no longer available. Please visit <https://builds.jabref.org/main/> for the latest build.
mode: upsert

@ -34,7 +34,6 @@ concurrency:
jobs:
build:
if: github.repository_owner == 'JabRef'
strategy:
fail-fast: false
matrix:
@ -69,16 +68,16 @@ jobs:
submodules: 'true'
show-progress: 'false'
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v3.0.2
uses: gittools/actions/gitversion/setup@v3.0.0
with:
versionSpec: "5.x"
- name: Run GitVersion
id: gitversion
uses: gittools/actions/gitversion/execute@v3.0.2
uses: gittools/actions/gitversion/execute@v3.0.0
- name: Setup JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Clean up keychain
run: |
@ -206,7 +205,7 @@ jobs:
if: ${{ (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) && (steps.checksecrets.outputs.secretspresent == 'YES') && ((matrix.os == 'ubuntu-latest') || ((matrix.os == 'macos-14') && !((startsWith(github.ref, 'refs/tags/') || inputs.notarization == true)))) }}
shell: bash
run: |
rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ || true
rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/
- name: Upload to GitHub workflow artifacts store (macOS)
if: (matrix.os == 'macos-14') && (steps.checksecrets.outputs.secretspresent == 'YES') && (startsWith(github.ref, 'refs/tags/') || inputs.notarization == true)
uses: actions/upload-artifact@v4

@ -32,13 +32,12 @@ concurrency:
jobs:
build:
if: github.repository_owner == 'JabRef'
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest, buildjet-8vcpu-ubuntu-2204-arm]
jdk: [23]
javafx: [24]
jdk: [22]
javafx: [23]
include:
- os: ubuntu-latest
displayName: linux
@ -85,12 +84,12 @@ jobs:
packages: pigz
version: 1.0
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v3.0.2
uses: gittools/actions/gitversion/setup@v3.0.0
with:
versionSpec: "5.x"
- name: Run GitVersion
id: gitversion
uses: gittools/actions/gitversion/execute@v3.0.2
uses: gittools/actions/gitversion/execute@v3.0.0
# JDK
- name: 'Set up JDK ${{ matrix.jdk }}'
@ -267,7 +266,7 @@ jobs:
shell: cmd
# for rsync installed by chocolatey, we need the ssh.exe delivered with that installation
run: |
rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/jdk-ea && rsync" -e 'C:\ProgramData\chocolatey\lib\rsync\tools\bin\ssh.exe -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/jdk-ea/ || true
rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/jdk-ea && rsync" -e 'C:\ProgramData\chocolatey\lib\rsync\tools\bin\ssh.exe -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/jdk-ea/
- name: Upload to builds.jabref.org (linux, macOS)
if: (matrix.os != 'windows-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (github.ref == 'refs/heads/main')
shell: bash

@ -81,16 +81,16 @@ jobs:
packages: pigz
version: 1.0
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v3.0.2
uses: gittools/actions/gitversion/setup@v3.0.0
with:
versionSpec: "5.x"
- name: Run GitVersion
id: gitversion
uses: gittools/actions/gitversion/execute@v3.0.2
uses: gittools/actions/gitversion/execute@v3.0.0
- name: Setup JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
@ -242,7 +242,7 @@ jobs:
shell: cmd
# for rsync installed by chocolatey, we need the ssh.exe delivered with that installation
run: |
rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'C:\ProgramData\chocolatey\lib\rsync\tools\bin\ssh.exe -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ || true
rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'C:\ProgramData\chocolatey\lib\rsync\tools\bin\ssh.exe -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/
- name: Upload to builds.jabref.org (linux, macOS)
# macOS: Negated condition of "Upload to GitHub workflow artifacts store (macOS)"
# Reason: We either upload the non-notarized files - or notarize the files later (and upload these later)
@ -250,7 +250,7 @@ jobs:
if: ${{ (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) && (steps.checksecrets.outputs.secretspresent == 'YES') && ((matrix.os == 'ubuntu-latest') || ((matrix.os == 'macos-13') && !((startsWith(github.ref, 'refs/tags/') || inputs.notarization == true)))) }}
shell: bash
run: |
rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ || true
rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/
- name: Upload to GitHub workflow artifacts store (macOS)
if: (matrix.os == 'macos-13') && (steps.checksecrets.outputs.secretspresent == 'YES') && (startsWith(github.ref, 'refs/tags/') || inputs.notarization == true)
uses: actions/upload-artifact@v4
@ -287,11 +287,11 @@ jobs:
BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }}
- name: Comment PR
if: (steps.checksecrets.outputs.secretspresent == 'YES')
uses: thollander/actions-comment-pull-request@v3
uses: thollander/actions-comment-pull-request@v2
with:
message: |
The build of this PR is available at <https://builds.jabref.org/pull/${{ github.event.pull_request.number }}/merge>.
comment-tag: download-link
comment_tag: download-link
mode: recreate
notarize: # outsourced in a separate job to be able to rerun if this fails for timeouts
name: macOS notarization

@ -15,7 +15,6 @@ concurrency:
jobs:
action:
if: github.repository_owner == 'JabRef'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'

@ -1,124 +0,0 @@
name: On labeled issue
on:
issues:
types:
- labeled
jobs:
Assigned:
# Triggered when manually assigned the label "📍 Assigned" to trigger the automatic unassignment after 30 days
name: "📍 Assigned"
if: ${{ github.event.label.name == '📍 Assigned' && github.repository_owner == 'JabRef' }}
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Move Issue to "Free to take" Column in "Candidates for University Projects"
uses: m7kvqbe1/github-action-move-issues@feat/skip-if-not-in-project-flag
with:
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
project-url: "https://github.com/orgs/JabRef/projects/3"
target-labels: "📍 Assigned"
target-column: "Free to take"
ignored-columns: ""
default-column: "Free to take"
skip-if-not-in-project: true
- name: Move Issue to "Free to take" Column in "Good First Issues"
uses: m7kvqbe1/github-action-move-issues@feat/skip-if-not-in-project-flag
with:
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
project-url: "https://github.com/orgs/JabRef/projects/5"
target-labels: "📍 Assigned"
target-column: "Free to take"
ignored-columns: ""
default-column: "Free to take"
skip-if-not-in-project: true
FirstTimeCodeContribution:
if: ${{ github.event.label.name == 'FirstTimeCodeContribution' && github.repository_owner == 'JabRef' }}
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: GreetingFirstTimeCodeContribution
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.issue.number || github.event.pull_request.number }}
body: |
Welcome to the vibrant world of open-source development with JabRef!
Newcomers, we're excited to have you on board. Start by exploring our [Contributing](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md) guidelines, and don't forget to check out our [workspace setup guidelines](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace) to get started smoothly.
In case you encounter failing tests during development, please check our [developer FAQs](https://devdocs.jabref.org/code-howtos/faq.html)!
Having any questions or issues? Feel free to ask here on GitHub. Need help setting up your local workspace? Join the conversation on [JabRef's Gitter chat](https://gitter.im/JabRef/jabref). And don't hesitate to open a (draft) pull request early on to show the direction it is heading towards. This way, you will receive valuable feedback.
⚠ Note that this issue will become unassigned if it isn't closed within **30 days**.
🔧 A maintainer can also add the **`Pinned`** label to prevent it from being unassigned automatically.
Happy coding! 🚀
- name: Move Issue to "Assigned" Column in "Candidates for University Projects"
uses: m7kvqbe1/github-action-move-issues@feat/skip-if-not-in-project-flag
with:
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
project-url: "https://github.com/orgs/JabRef/projects/3"
target-labels: "FirstTimeCodeContribution"
target-column: "Assigned"
ignored-columns: ""
default-column: "Free to take"
skip-if-not-in-project: true
- name: Move Issue to "Assigned" Column in "Good First Issues"
uses: m7kvqbe1/github-action-move-issues@feat/skip-if-not-in-project-flag
with:
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
project-url: "https://github.com/orgs/JabRef/projects/5"
target-labels: "FirstTimeCodeContribution"
target-column: "Assigned"
ignored-columns: ""
default-column: "Free to take"
skip-if-not-in-project: true
good-first-issue:
name: "good first issue"
if: "${{ github.event.label.name == 'good first issue' && github.repository_owner == 'JabRef' }}"
runs-on: ubuntu-latest
steps:
- name: "good first issue"
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ITEM_ADD }}
run: |
ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH")
gh project item-add 5 --owner JabRef --url $ISSUE_URL
needs-refinement:
if: github.event.label.name == 'needs-refinement' && github.repository_owner == 'JabRef'
runs-on: ubuntu-latest
steps:
- name: needs-refinement
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ITEM_ADD }}
run: |
ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH")
gh project item-add 15 --owner JabRef --url $ISSUE_URL
status-freeze:
name: "status: freeze"
if: "${{ github.event.label.name == 'status: freeze' && github.repository_owner == 'JabRef' }}"
runs-on: ubuntu-latest
steps:
- name: "status: freeze"
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ITEM_ADD }}
run: |
ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH")
gh project item-add 9 --owner JabRef --url $ISSUE_URL
ui:
if: "${{ github.event.label.name == 'ui' && github.repository_owner == 'JabRef' }}"
runs-on: ubuntu-latest
steps:
- name: ui
env:
GH_DEBUG: api
GH_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ITEM_ADD }}
run: |
ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH")
echo $ISSUE_URL
gh project item-add 8 --owner JabRef --url $ISSUE_URL

@ -1,23 +0,0 @@
name: On labeled PR
on:
pull_request:
types:
- labeled
jobs:
automerge:
name: Auto Merge
if: "${{ github.event.label.name == 'automerge' && github.repository_owner == 'JabRef' }}"
runs-on: ubuntu-latest
steps:
- name: Approve PR
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GH_TOKEN_JABREF_MACHINE_PR_APPROVE}}
- name: Merge PR
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GH_TOKEN_UPDATE_GRADLE_WRAPPER}}

@ -1,34 +0,0 @@
name: On unlabeled issue
on:
issues:
types:
- unlabeled
jobs:
FirstTimeCodeContribution_or_Assigned:
if: ${{ ((github.event.label.name == 'FirstTimeCodeContribution') || (github.event.label.name == '📍 Assigned')) && github.repository_owner == 'JabRef' }}
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Move Issue to "Free to take" Column in "Candidates for University Projects"
uses: m7kvqbe1/github-action-move-issues@feat/skip-if-not-in-project-flag
with:
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
project-url: "https://github.com/orgs/JabRef/projects/3"
target-labels: "📍 Assigned"
target-column: "Assigned"
ignored-columns: ""
default-column: "Free to take"
skip-if-not-in-project: true
- name: Move Issue to "Free to take" Column in "Good First Issues"
uses: m7kvqbe1/github-action-move-issues@feat/skip-if-not-in-project-flag
with:
github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }}
project-url: "https://github.com/orgs/JabRef/projects/5"
target-labels: "📍 Assigned"
target-column: "Assigned"
ignored-columns: ""
default-column: "Free to take"
skip-if-not-in-project: true

@ -6,8 +6,6 @@ on:
- 'docs/**'
- '.github/workflows/pages.yml'
push:
branches:
main
paths:
- 'docs/**'
- '.github/workflows/pages.yml'
@ -19,8 +17,9 @@ permissions:
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}"
group: "pages"
cancel-in-progress: true
jobs:
@ -34,9 +33,9 @@ jobs:
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3' # Not needed with a .ruby-version file
ruby-version: '2.7' # Not needed with a .ruby-version file
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
cache-version: 0 # Increment this number if you need to re-download cached gems
cache-version: 1 # Increment this number if you need to re-download cached gems
working-directory: docs/
- name: Setup Pages
id: pages

@ -14,7 +14,7 @@ on:
jobs:
comment:
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#running-a-workflow-based-on-the-conclusion-of-another-workflow
if: ${{ github.event.workflow_run.conclusion == 'failure' && (github.repository_owner == 'JabRef') }}
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
runs-on: ubuntu-latest
permissions:
actions: read
@ -34,27 +34,16 @@ jobs:
PR_NUMBER=$(cat pr_number.txt)
echo "Read PR number $PR_NUMBER"
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
- uses: actions/checkout@v4
- name: Is PR from forked?
if: ${{ steps.read-pr_number.outputs.pr_number != '' }}
id: isCrossRepository
run: |
isCrossRepository=$(gh pr view $pr_number --json isCrossRepository --jq '.isCrossRepository')
echo "Got isCrossRepository $isCrossRepository"
echo isCrossRepository=$isCrossRepository >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ github.token }}
pr_number: ${{ steps.read-pr_number.outputs.pr_number }}
- name: Checkout
if: ${{ steps.read-pr_number.outputs.pr_number != '' && steps.isCrossRepository.outputs.isCrossRepository == 'true' }}
if: ${{ steps.read-pr_number.outputs.pr_number != '' }}
uses: actions/checkout@v4
with:
fetch-depth: '0'
show-progress: 'false'
token: ${{ secrets.GITHUB_TOKEN }}
- name: jbang
if: ${{ steps.read-pr_number.outputs.pr_number != '' && steps.isCrossRepository.outputs.isCrossRepository == 'true' }}
uses: jbangdev/jbang-action@v0.119.0
if: ${{ steps.read-pr_number.outputs.pr_number != '' }}
uses: jbangdev/jbang-action@v0.117.1
with:
script: ghprcomment@koppor/ghprcomment
scriptargs: "-r JabRef/jabref -p ${{ steps.read-pr_number.outputs.pr_number }} -w ${{ github.event.workflow_run.id }}"

@ -48,7 +48,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

@ -37,7 +37,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Run checkstyle reporter
uses: dbelyaev/action-checkstyle@master
@ -65,7 +65,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
@ -87,7 +87,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
@ -109,7 +109,7 @@ jobs:
submodules: 'false'
show-progress: 'false'
- name: markdownlint-cli2-action
uses: DavidAnson/markdownlint-cli2-action@v18
uses: DavidAnson/markdownlint-cli2-action@v16
with:
globs: |
*.md
@ -131,7 +131,7 @@ jobs:
export PATH=$PATH:$HOME/.jbang/bin
# run heylogs verification
jbang com.github.nbbrd.heylogs:heylogs-cli:0.9.2:bin check CHANGELOG.md > heylogs.txt || true
jbang com.github.nbbrd.heylogs:heylogs-cli:0.7.2:bin check CHANGELOG.md > heylogs.txt || true
# improve output
sed -i 's/all-h2-contain-a-version/all-h2-contain-a-version (ignored)/' heylogs.txt
@ -175,7 +175,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
@ -218,7 +218,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
@ -259,7 +259,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
@ -309,7 +309,7 @@ jobs:
if: github.ref == 'refs/heads/main'
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
@ -322,7 +322,7 @@ jobs:
CI: "true"
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
DBMS: "postgresql"
- uses: codecov/codecov-action@v5
- uses: codecov/codecov-action@v4
if: (github.ref == 'refs/heads/main') && (steps.checksecrets.outputs.secretspresent == 'YES')
with:
token: ${{ secrets.CODECOV_TOKEN }}
@ -332,24 +332,6 @@ jobs:
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
requirements_coverage:
name: "Validate requirement coverage"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
show-progress: 'false'
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- run: ./gradlew traceRequirements
- if: failure()
run: cat build/reports/tracing.txt
# This is https://github.com/marketplace/actions/gradle-wrapper-validation
# It ensures that the jar file is from gradle and not by a strange third party.
gradlevalidation:
@ -380,7 +362,7 @@ jobs:
uses: actions/github-script@v7
with:
script: |
core.setFailed('Pull requests should come from a branch other than "main"\n\n👉 Please read [the CONTRIBUTING guide](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md#contributing) carefully again. 👈')
core.setFailed('Pull requests should come from a branch other than "main"\n\n👉 Please read https://devdocs.jabref.org/contributing again carefully. 👈')
upload-pr-number:
runs-on: ubuntu-latest

@ -7,7 +7,6 @@ on:
jobs:
update-gradle-wrapper:
if: github.repository_owner == 'JabRef'
runs-on: ubuntu-latest
steps:
@ -15,11 +14,11 @@ jobs:
- name: Setup JDK
uses: actions/setup-java@v4
with:
java-version: 23.0.1
java-version: 21.0.2
distribution: 'temurin'
- name: Update Gradle Wrapper
uses: gradle-update/update-gradle-wrapper-action@v2
uses: gradle-update/update-gradle-wrapper-action@v1
with:
labels: dependencies
repo-token: ${{ secrets.GH_TOKEN_UPDATE_GRADLE_WRAPPER }}

@ -5,4 +5,4 @@ FROM gitpod/workspace-full
# All available versions can be listed using sdk ls java
# More information about SDKMAN available at https://github.com/sdkman/sdkman-cli#sdkman-cli
RUN bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh \
&& sdk install java 23-open"
&& sdk install java 21-open"

@ -1,6 +1,6 @@
{
"recommendations": [
"davidanson.vscode-markdownlint",
"ltex-plus.vscode-ltex-plus"
"valentjn.vscode-ltex"
]
}

@ -2,6 +2,4 @@ Checkstyle
CouchDB
JabDrive
JabRef
OpenFastTrace
OpenRewrite
Temurin

@ -11,16 +11,13 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
### Added
- We added a "view as BibTeX" option before importing an entry from the citation relation tab. [#11826](https://github.com/JabRef/jabref/issues/11826)
- We added probable search hits instead of exact matches. Sorting by hit score can be done by the new score table column. [#11542](https://github.com/JabRef/jabref/pull/11542)
- We added support finding LaTeX-encoded special characters based on plain Unicode and vice versa. [#11542](https://github.com/JabRef/jabref/pull/11542)
- When a search hits a file, the file icon of that entry is changed accordingly. [#11542](https://github.com/JabRef/jabref/pull/11542)
- We added an AI-based chat for entries with linked PDF files. [#11430](https://github.com/JabRef/jabref/pull/11430)
- We added an AI-based summarization possibility for entries with linked PDF files. [#11430](https://github.com/JabRef/jabref/pull/11430)
- We added an AI section in JabRef's [preferences](https://docs.jabref.org/ai/preferences). [#11430](https://github.com/JabRef/jabref/pull/11430)
- We added AI providers: OpenAI, Mistral AI, Hugging Face and Google. [#11430](https://github.com/JabRef/jabref/pull/11430), [#11736](https://github.com/JabRef/jabref/pull/11736)
- We added AI providers: [Ollama](https://docs.jabref.org/ai/local-llm#step-by-step-guide-for-ollama) and GPT4All, which add the possibility to use local LLMs privately on your own device. [#11430](https://github.com/JabRef/jabref/pull/11430), [#11870](https://github.com/JabRef/jabref/issues/11870)
- We added support for selecting and using CSL Styles in JabRef's OpenOffice/LibreOffice integration for inserting bibliographic and in-text citations into a document. [#2146](https://github.com/JabRef/jabref/issues/2146), [#8893](https://github.com/JabRef/jabref/issues/8893)
- We added "Tools > New library based on references in PDF file" ... to create a new library based on the references section in a PDF file. [#11522](https://github.com/JabRef/jabref/pull/11522)
- We added Tools > New library based on references in PDF file... to create a new library based on the references section in a PDF file. [#11522](https://github.com/JabRef/jabref/pull/11522)
- When converting the references section of a paper (PDF file), more than the last page is treated. [#11522](https://github.com/JabRef/jabref/pull/11522)
- Added the functionality to invoke offline reference parsing explicitly. [#11565](https://github.com/JabRef/jabref/pull/11565)
- The dialog for [adding an entry using reference text](https://docs.jabref.org/collect/newentryfromplaintext) is now filled with the clipboard contents as default. [#11565](https://github.com/JabRef/jabref/pull/11565)
@ -29,58 +26,26 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We enabled creating a new file link manually. [#11017](https://github.com/JabRef/jabref/issues/11017)
- We added a toggle button to invert the selected groups. [#9073](https://github.com/JabRef/jabref/issues/9073)
- We reintroduced the floating search in the main table. [#4237](https://github.com/JabRef/jabref/issues/4237)
- We improved [cleanup](https://docs.jabref.org/finding-sorting-and-cleaning-entries/cleanupentries) of `arXiv` IDs in distributed in the fields `note`, `version`, `institution`, and `eid` fields. [#11306](https://github.com/JabRef/jabref/issues/11306)
- We added a switch not to store the linked file URL, because it caused troubles at other apps. [#11735](https://github.com/JabRef/jabref/pull/11735)
- When starting a new SLR, the selected catalogs now persist within and across JabRef sessions. [koppor#614](https://github.com/koppor/jabref/issues/614)
- We added support for drag'n'drop on an entry in the maintable to an external application to get the entry preview dropped. [#11846](https://github.com/JabRef/jabref/pull/11846)
- We added the functionality to double click on a [LaTeX citation](https://docs.jabref.org/advanced/entryeditor/latex-citations) to jump to the respective line in the LaTeX editor. [#11996](https://github.com/JabRef/jabref/issues/11996)
- We added a different background color to the search bar to indicate when the search syntax is wrong. [#11658](https://github.com/JabRef/jabref/pull/11658)
- We added a setting which always adds the literal "Cited on pages" text before each JStyle citation. [#11691](https://github.com/JabRef/jabref/pull/11732)
- We added a new plain citation parser that uses LLMs. [#11825](https://github.com/JabRef/jabref/issues/11825)
- We added support for modifier keys when dropping a file on an entry in the main table. [#12001](https://github.com/JabRef/jabref/pull/12001)
- We added an importer for SSRN URLs. [#12021](https://github.com/JabRef/jabref/pull/12021)
- We added a compare button to the duplicates in the citation relations tab to open the "Possible duplicate entries" window. [#11192](https://github.com/JabRef/jabref/issues/11192)
- We added automatic browser extension install on Windows for Chrome and Edge. [#6076](https://github.com/JabRef/jabref/issues/6076)
- We added support to automatically open a `.bib` file in the current/parent folder if no other library is opened. [koppor#377](https://github.com/koppor/jabref/issues/377)
- We added a search bar for filtering keyboard shortcuts. [#11686](https://github.com/JabRef/jabref/issues/11686)
- By double clicking on a local citation in the Citation Relations Tab you can now jump the linked entry. [#11955](https://github.com/JabRef/jabref/pull/11955)
- We use the menu icon for background tasks as a progress indicator to visualise an import's progress when dragging and dropping several PDF files into the main table. [#12072](https://github.com/JabRef/jabref/pull/12072)
### Changed
- The search syntax is changed to [Apache Lucene syntax](https://lucene.apache.org/core/9_11_1/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Overview) (also to be similar to the [online search syntax](https://docs.jabref.org/collect/import-using-online-bibliographic-database#search-syntax)). [#11542](https://github.com/JabRef/jabref/pull/11542/)
- When searching using a regular expression, one needs to enclose the search string in `/`. [#11542](https://github.com/JabRef/jabref/pull/11542/)
- A search in "any" fields ignores the [groups](https://docs.jabref.org/finding-sorting-and-cleaning-entries/groups). [#7996](https://github.com/JabRef/jabref/issues/7996)
- When a communication error with an [online service](https://docs.jabref.org/collect/import-using-online-bibliographic-database) occurs, JabRef displays the HTTP error. [#11223](https://github.com/JabRef/jabref/issues/11223)
- The Pubmed/Medline Plain importer now imports the PMID field as well [#11488](https://github.com/JabRef/jabref/issues/11488)
- The 'Check for updates' menu bar button is now always enabled. [#11485](https://github.com/JabRef/jabref/pull/11485)
- JabRef respects the [configuration for storing files relative to the .bib file](https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#directories-for-files) in more cases. [#11492](https://github.com/JabRef/jabref/pull/11492)
- JabRef does not show finished background tasks in the status bar popup. [#11821](https://github.com/JabRef/jabref/pull/11821)
- JabRef does not show finished background tasks in the status bar popup. [#11574](https://github.com/JabRef/jabref/pull/11574)
- We enhanced the indexing speed. [#11502](https://github.com/JabRef/jabref/pull/11502)
- When dropping a file into the main table, after copy or move, the file is now put in the [configured directory and renamed according to the configured patterns](https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#filename-format-and-file-directory-pattern). [#12001](https://github.com/JabRef/jabref/pull/12001)
- ⚠️ Renamed command line parameters `embeddBibfileInPdf` to `embedBibFileInPdf`, `writeMetadatatoPdf` to `writeMetadataToPdf`, and `writeXMPtoPdf` to `writeXmpToPdf`. [#11575](https://github.com/JabRef/jabref/pull/11575)
- The browse button for a Custom theme now opens in the directory of the current used CSS file. [#11597](https://github.com/JabRef/jabref/pull/11597)
- The browse button for a Custom exporter now opens in the directory of the current used exporter file. [#11717](https://github.com/JabRef/jabref/pull/11717)
- ⚠️ We relaxed the escaping requirements for [bracketed patterns](https://docs.jabref.org/setup/citationkeypatterns), which are used for the [citaton key generator](https://docs.jabref.org/advanced/entryeditor#autogenerate-citation-key) and [filename and directory patterns](https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#auto-linking-files). One only needs to write `\"` if a quote sign should be escaped. All other escapings are not necessary (and working) any more. [#11967](https://github.com/JabRef/jabref/pull/11967)
- When importing BibTeX data starging from on a PDF, the XMP metadata takes precedence over Grobid data. [#11992](https://github.com/JabRef/jabref/pull/11992)
- JabRef now uses TLS 1.2 for all HTTPS connections. [#11852](https://github.com/JabRef/jabref/pull/11852)
- We improved the functionality of getting BibTeX data out of PDF files. [#11999](https://github.com/JabRef/jabref/issues/11999)
- We improved the display of long messages in the integrity check dialog. [#11619](https://github.com/JabRef/jabref/pull/11619)
- We improved the undo/redo buttons in the main toolbar and main menu to be disabled when there is nothing to undo/redo. [#8807](https://github.com/JabRef/jabref/issues/8807)
- We improved the DOI detection in PDF imports. [#11782](https://github.com/JabRef/jabref/pull/11782)
- We improved the performance when pasting and importing entries in an existing library. [#11843](https://github.com/JabRef/jabref/pull/11843)
- When fulltext search is selected but indexing is deactivated, a dialog is now shown asking if the user wants to enable indexing now [#9491](https://github.com/JabRef/jabref/issues/9491)
- We changed instances of 'Search Selected' to 'Search Pre-configured' in Web Search Preferences UI. [#11871](https://github.com/JabRef/jabref/pull/11871)
- We added a new CSS style class `main-table` for the main table. [#11881](https://github.com/JabRef/jabref/pull/11881)
- When renaming a file, the old extension is now used if there is none provided in the new name. [#11903](https://github.com/JabRef/jabref/issues/11903)
- When importing a file using "Find Unlinked Files", when one or more file directories are available, the file path will be relativized where possible [koppor#549](https://github.com/koppor/jabref/issues/549)
- We added minimum window sizing for windows dedicated to creating new entries [#11944](https://github.com/JabRef/jabref/issues/11944)
- We changed the name of the library-based file directory from 'General File Directory' to 'Library-specific File Directory' per issue. [#571](https://github.com/koppor/jabref/issues/571)
- We changed the defualt [unwanted charachters](https://docs.jabref.org/setup/citationkeypatterns#removing-unwanted-characters) in the citation key generator and allow a dash (`-`) and colon (`:`) being part of a citation key. [#12144](https://github.com/JabRef/jabref/pull/12144)
- The CitationKey column is now a default shown column for the entry table. [#10510](https://github.com/JabRef/jabref/issues/10510)
- We disabled the actions "Open Terminal here" and "Reveal in file explorer" for unsaved libraries. [#11920](https://github.com/JabRef/jabref/issues/11920)
### Fixed
- We fixed an issue where certain actions were not disabled when no libraries were open. [#11923](https://github.com/JabRef/jabref/issues/11923)
- We fixed an issue where the "Check for updates" preference was not saved. [#11485](https://github.com/JabRef/jabref/pull/11485)
- We fixed an issue where an exception was thrown after changing "show preview as a tab" in the preferences. [#11515](https://github.com/JabRef/jabref/pull/11515)
- We fixed an issue where JabRef put file paths as absolute path when an entry was created using drag and drop of a PDF file. [#11173](https://github.com/JabRef/jabref/issues/11173)
@ -99,31 +64,13 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We fixed an issue where the full-text search results were incomplete. [#8626](https://github.com/JabRef/jabref/issues/8626)
- We fixed an issue where search result highlighting was incorrectly highlighting the boolean operators. [#11595](https://github.com/JabRef/jabref/issues/11595)
- We fixed an issue where search result highlighting was broken at complex searches. [#8067](https://github.com/JabRef/jabref/issues/8067)
- We fixed an exception when searching for unlinked files. [#11731](https://github.com/JabRef/jabref/issues/11731)
- We fixed an issue with the link to the full text at the BVB fetcher. [#11852](https://github.com/JabRef/jabref/pull/11852)
- We fixed an issue where two contradicting notifications were shown when cutting an entry in the main table. [#11724](https://github.com/JabRef/jabref/pull/11724)
- We fixed an issue where unescaped braces in the arXiv fetcher were not treated. [#11704](https://github.com/JabRef/jabref/issues/11704)
- We fixed an issue where HTML instead of the fulltext pdf was downloaded when importing arXiv entries. [#4913](https://github.com/JabRef/jabref/issues/4913)
- We fixed an issue where the keywords and crossref fields were not properly focused. [#11177](https://github.com/JabRef/jabref/issues/11177)
- We fixed handling of `\"` in [bracketed patterns](https://docs.jabref.org/setup/citationkeypatterns) containing a RegEx. [#11967](https://github.com/JabRef/jabref/pull/11967)
- We fixed an issue where the Undo/Redo buttons were active even when all libraries are closed. [#11837](https://github.com/JabRef/jabref/issues/11837)
- We fixed an issue where recently opened files were not displayed in the main menu properly. [#9042](https://github.com/JabRef/jabref/issues/9042)
- We fixed an issue where the DOI lookup would show an error when a DOI was found for an entry. [#11850](https://github.com/JabRef/jabref/issues/11850)
- We fixed an issue where <kbd>Tab</kbd> cannot be used to jump to next field in some single-line fields. [#11785](https://github.com/JabRef/jabref/issues/11785)
- We fixed an issue where the "Do not ask again" checkbox was not working, when asking for permission to use Grobid [koppor#556](https://github.com/koppor/jabref/issues/566).
- We fixed an issue where we display warning message for moving attached open files. [#10121](https://github.com/JabRef/jabref/issues/10121)
- We fixed an issue where it was not possible to select selecting content of other user's comments.[#11106](https://github.com/JabRef/jabref/issues/11106)
- We fixed an issue where web search preferences "Custom API key" table modifications not discarded. [#11925](https://github.com/JabRef/jabref/issues/11925)
- We fixed an issue when opening attached files in [extra file columns](https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#adding-additional-columns-to-entry-table-for-file-types). [#12005](https://github.com/JabRef/jabref/issues/12005)
- We fixed an issue where trying to open a library from a failed mounted directory on Mac would cause an error. [#10548](https://github.com/JabRef/jabref/issues/10548)
- We fixed an issue when the preview was out of sync. [#9172](https://github.com/JabRef/jabref/issues/9172)
- We fixed an issue where identifier paste couldn't work with Unicode REPLACEMENT CHARACTER. [#11986](https://github.com/JabRef/jabref/issues/11986)
### Removed
- We removed support for case-sensitive and exact search. [#11542](https://github.com/JabRef/jabref/pull/11542)
- We removed the description of search strings. [#11542](https://github.com/JabRef/jabref/pull/11542)
- We removed support for importing using the SilverPlatterImporter (`Record INSPEC`). [#11576](https://github.com/JabRef/jabref/pull/11576)
- We removed support for automatically generating file links using the CLI (`--automaticallySetFileLinks`).

@ -1,198 +1,17 @@
# Contributing
General overview about contributing for non-programmers is available at <https://docs.jabref.org/contributing>.
When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change.
We welcome contributions to JabRef and encourage you to follow the GitHub workflow specified below.
If you are not familiar with this type of workflow, take a look at GitHub's excellent overview on the [GitHub flow](https://docs.github.com/en/get-started/using-github/github-flow) and the explanation of [Feature Branch Workflow](https://atlassian.com/git/tutorials/comparing-workflows#feature-branch-workflow) for the idea behind this kind of development.
Support on **code contribution** is available at <https://devdocs.jabref.org/contributing#contribute-code>.
Before you start, get the JabRef code on your local machine.
Detailed instructions about this step can be found in our [guidelines for setting up a local workspace](getting-into-the-code/guidelines-for-setting-up-a-local-workspace/).
## Table of Contents
* [Choosing a task](#choosing-a-task-)
* [Getting a task assigned](#getting-a-task-assigned)
* [Pull Request Process](#pull-request-process)
* [Requirements on the pull request and code](#requirements-on-the-pull-request-and-code)
* [After submission of a pull request](#after-submission-of-a-pull-request)
* [Development hints](#development-hints)
## Choosing a task [![Join the chat at https://gitter.im/JabRef/jabref](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/JabRef/jabref)
In general, we offer small issues perfect for aspiring developers.
These tasks provide an opportunity to learn how to set up your local workspace, create your first pull request on GitHub, and contribute towards solving minor problems or making small enhancements in JabRef.
It is essential to note that JabRef's issues vary in difficulty.
Some are simpler, while others are more complex. Our primary aim is to guide you through the code, ensuring that the understanding scope remains manageable. Sometimes, grasping the code might demand more effort than actually writing lines of code.
### I am a student and I want to start with something easy
We collect good issues to start with at our [list of good first issues](https://github.com/orgs/JabRef/projects/5/views/1).
### I am a student and I want to choose from a curated list of university projects
Take a look at [JabRef's candidates for university projects](https://github.com/orgs/JabRef/projects/3). There, a list of possible projects to work on during a teaching period is offered.
### I am a lecturer
If you ask yourself how to integrate JabRef into your class, please read the [documentation about how to integrate JabRef into a class of software engineering training](https://devdocs.jabref.org/teaching.html#jabref-and-software-engineering-training).
As student, you may notify your lecturer about this possibility.
### I want something with huge impact
Look at the discussions in our forum about [new features](https://discourse.jabref.org/c/features/6).
Find an interesting topic, discuss it and start contributing.
Alternatively, you can check out [JabRef's projects page at GitHub](https://github.com/JabRef/jabref/projects?query=is%3Aopen).
Although, of course, you can choose to work on ANY issue, choosing from the projects page has the advantage that these issues have already been categorized, sorted and screened by JabRef maintainers.
A typical subclassifications scheme is "priority" (high, normal and low). Fixing high priority issues is preferred.
### I want to know how to contribute code and set up my workspace
Check out the [documentation for developers](https://devdocs.jabref.org/contributing.html#contribute-code)
### I want to improve the developer's documentation
For improving developer's documentation, go on at the [docs/ subdirectory of JabRef's code](https://github.com/JabRef/jabref/tree/main/docs) and edit the file.
GitHub offers a good guide at [Editing files in another user's repository](https://help.github.com/en/github/managing-files-in-a-repository/editing-files-in-another-users-repository).
One can also add [callouts](https://just-the-docs.github.io/just-the-docs-tests/components/callouts/).
## Getting a task assigned
Comment on the issue you want to work at with `/assign-me`.
GitHub will then automatically assign you.
General overview about contributing for programmers and non-programmers is available at <https://contribute.jabref.org>.
## Pull Request Process
1. Follow the steps at [Pre Condition 3: Code on the local machine](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/pre-03-code.html) to a) create a fork and b) have the fork checked out on your local machine
2. Ensure that you followed the [steps to set up a local workspace](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/) to have the code running properly in IntelliJ.
3. **Create a new branch** (such as `fix-for-issue-121`). Be sure to create a **separate branch** for each improvement you implement.
4. Work on the **new branch — not the `main` branch.** Refer to our [code how-tos](https://devdocs.jabref.org/code-howtos) if you have questions about your implementation.
5. Create a [pull request to JabRef main repository](https://github.com/JabRef/jabref/pulls).
For an overview on the concept of pull requests, take a look at GitHub's [pull request help documentation](https://help.github.com/articles/about-pull-requests/).
1. Ensure that you followed the requirements listed below. They are not too hard, they merely support the maintainers to focus on supportive feedback than just stating the obvious.
2. For text inspirations, consider [How to write the perfect pull request](https://github.com/blog/1943-how-to-write-the-perfect-pull-request).
3. In case your pull request is not yet complete or not yet ready for review, create a [draft pull request](https://github.blog/2019-02-14-introducing-draft-pull-requests/) instead.
6. Wait for feedback of the developers
7. Address the feedback of the developers
8. After two developers gave their green flag, the pull request will be merged.
In case you have any questions, please
1. comment on the issue,
2. show up in our [Gitter chat](https://gitter.im/JabRef/jabref), or
3. show your current code using a draft pull request and ask questions.
We favor looking into your code using a draft pull request, because we can then also load the code into our IDE.
As counterexample, if you provide us with a screenshot of your changes, we cannot run it in our IDE.
### Requirements on the pull request and code
#### Test your code
We know that writing test cases takes a lot of time.
Nevertheless, we rely on our test cases to ensure that a bug fix or a feature implementation does not break anything.
For UI changes, we know that test cases are hard to write.
Therefore, you can omit them.
However, please at least add a screenshot showing your changes to the request.
<!-- In case you do not have time to add a test case, we nevertheless ask you to at least run `gradlew check` to ensure that your change does not break anything else. -->
#### Write a good commit message
See [good commit message](https://github.com/joelparkerhenderson/git-commit-message) or [commit guidelines section of Pro Git](http://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines). For the curious: [Why good commit messages matter!](https://cbea.ms/git-commit/). The first line of your commit message is automatically taken as the title for the pull-request. All other lines make up the body of the pull request. Add the words `fixes #xxx` to your PR to auto-close the corresponding issue.
#### Add your change to `CHANGELOG.md`
You should edit the [`CHANGELOG.md`](https://github.com/JabRef/jabref/blob/main/CHANGELOG.md#changelog) file located in the root directory of the JabRef source. Add a line with your changes in the appropriate section.
If you did internal refactorings or improvements not visible to the user (e.g., UI, .bib file), then you don't need to put an entry there.
#### Author credits
Please, **do not add yourself at JavaDoc's `@authors`**.
The contribution information is tracked via the version control system and shown at [https://github.com/JabRef/jabref/graphs/contributors](https://github.com/JabRef/jabref/graphs/contributors).
We also show all contributors in our blog posts. See [Release 5.15 blog post](https://blog.jabref.org/2024/07/16/JabRef5-15/) for an example.
Your contribution is considered being made under [MIT license](https://tldrlegal.com/license/mit-license).
#### Notes on AI usage
Please keep these two principles in mind when you contribute:
1. Never let an LLM speak for you.
2. Never let an LLM think for you.
More reading on that is available at <https://roe.dev/blog/using-ai-in-open-source>.
We reserve the right to reject pull requests that contain little or no genuine and original contribution from the contributor.
### After submission of a pull request
After you submitted a pull request, automated checks will run.
You may see "Some checks were not successful".
You can click on failing checks to see more information about why they failed.
Then, please look into them and handle accordingly.
Afterwards, you will receive comments on your pull request.
The pull request may be approved immediatly, or a reviewer may request changes.
In that case, please implement the requested changes.
After implementing changes, commit to the branch your pull request is *from* and push.
The pull request will automatically be updated with your changes.
Your commits will also be automatically squashed upon the pull request being accepted.
Please **Never ever close a pull request and open a new one** -
This causes unessesary work on our side, and is not in the the style of the GitHub open source community.
You can push any changes you need to make to the branch your pull request is *from*.
These changes will be automatically added to your pull request.
### Development hints
#### When adding an external dependency
Please try to use a version available at JCenter and add it to `build.gradle`.
In any case, describe the library at [`external-libraries.md`](https://github.com/JabRef/jabref/blob/main/external-libraries.md#external-libraries).
We need that information for our package maintainers (e.g., those of the [debian package](https://tracker.debian.org/pkg/jabref)).
#### When making an architectural decision
In case you add a library or do major code rewrites, we ask you to document your decision. Recommended reading: [https://adr.github.io/](https://adr.github.io).
We simply ask to create a new markdown file in `docs/adr` following the template presented at [https://adr.github.io/madr/](https://adr.github.io/madr/).
You can link that ADR using `@ADR({num})` as annotation.
#### When adding a new `Localization.lang` entry
Add new `Localization.lang("KEY")` to a Java file. The tests will fail. In the test output a snippet is generated, which must be added to the English translation file.
Example:
```text
java.lang.AssertionError: DETECTED LANGUAGE KEYS WHICH ARE NOT IN THE ENGLISH LANGUAGE FILE
PASTE THESE INTO THE ENGLISH LANGUAGE FILE
[
Opens\ JabRef's\ Twitter\ page=Opens JabRef's Twitter page
]
Expected :[]
Actual :[Opens\ JabRef's\ Twitter\ page (src\main\java\org\jabref\gui\JabRefFrame.java LANG)]
```
Add the above snippet to the English translation file located at `src/main/resources/l10n/JabRef_en.properties`.
[Crowdin](https://crowdin.com/project/jabref) will automatically pick up the new string and add it to the other translations.
You can also directly run the specific test in your IDE.
The test "`LocalizationConsistencyTest`" is placed under `src/test/java/org.jabref.logic.l10n/LocalizationConsistencyTest.java`.
Find more information in the [JabRef developer docs](https://devdocs.jabref.org/code-howtos/localization.html).
#### **Format of keyboard shortcuts**
In Markdown files (e.g., `CHANGELOG.md`), sometimes keyboard shortcuts need to be added.
Example: `<kbd>Ctrl</kbd> + <kbd>Enter</kbd>`
In case you add keys to the changelog, please follow these rules:
* `<kbd>` tag for each key
* First letter of key capitalized
* Combined keys separated by `+`
* Spaces before and after separator `+`
1. Understand the basics listed at <https://devdocs.jabref.org/contributing#contribute-code>.
2. Follow the "formal requirements". They are not too hard, they merely support the maintainers to focus on supportive feedback than just stating the obvious. They also have helpful hints how to work with localization.
3. Create a pull request. You can create a draft pull request to enable automatic checks.
4. Wait for feedback of the developers
5. Address the feedback of the developers
6. After two developers gave their green flag, the pull request will be merged.

@ -38,46 +38,45 @@ These third parties may log additional information besides your IP address and t
These third-party services are the following:
| Service | Privacy Policy |
|------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|
| [ACM](https://www.acm.org/) | <https://www.acm.org/privacy-policy> |
| [ACS Publications](https://pubs.acs.org/) | <https://www.acs.org/privacy.html> |
| [APS Advancing Physics](https://harvest.aps.org/) | <https://www.aps.org/about/privacy.cfm#privacy> |
| [arXiv.org](https://arxiv.org/) | <https://info.arxiv.org/help/policies/privacy_policy.html> |
| [Bibliotheksverbund Bayern](https://www.bib-bvb.de/) | <https://www.bib-bvb.de/web/guest/datenschutzerklaerung-bvb-homepage> |
| [Biodiversity Heritage Library](https://www.biodiversitylibrary.org/) | <https://www.si.edu/Privacy> |
| [Collection of Computer Science Bibliographies](https://en.wikipedia.org/wiki/Collection_of_Computer_Science_Bibliographies) | **currently unavailable**, offline |
| [CrossRef](https://www.crossref.org/) | <https://www.crossref.org/operations-and-sustainability/privacy/> |
| [dblp](https://dblp.uni-trier.de/) | <https://dblp.uni-trier.de/db/about/privacy.html> |
| [DJL (Deep Java Library)](https://djl.ai/) | <https://github.com/deepjavalibrary/djl/discussions/3370#discussioncomment-10233632> |
| [Directory of Open Access Books](https://www.doabooks.org/) | <https://www.doabooks.org/en/resources/accessibility> |
| [Digitala Vetenskapliga Arkivet](https://www.diva-portal.org/) | <https://www.uu.se/en/about-uu/data-protection-policy/> |
| [DOI Foundation](https://www.doi.org/) | <https://www.doi.org/privacy-policy/> |
| [Elsevier](https://www.elsevier.com/) | <https://www.elsevier.com/legal/privacy-policy> |
| [Google Gemini](https://ai.google.dev/gemini-api) | <https://ai.google.dev/gemini-api/terms> |
| [Google Scholar](https://scholar.google.com/) | <https://policies.google.com/privacy> |
| [Gemeinsamer Verbundkatalog](https://www.gbv.de/) | <https://www.gbv.de/datenschutz> |
| [Hugging Face](https://huggingface.co/) | <https://huggingface.co/privacy> |
| [IACR](https://www.iacr.org/) | <https://www.iacr.org/privacy.html> |
| [IEEEXplore](https://ieeexplore.ieee.org/Xplore/home.jsp) | <https://www.ieee.org/security-privacy.html> |
| Service | Privacy Policy |
|------------------------------------------------------------------------------------------------------------------------------|----------------|
| [ACM](https://www.acm.org/) | <https://www.acm.org/privacy-policy> |
| [ACS Publications](https://pubs.acs.org/) | <https://www.acs.org/privacy.html> |
| [APS Advancing Physics](https://harvest.aps.org/) | <https://www.aps.org/about/privacy.cfm#privacy> |
| [arXiv.org](https://arxiv.org/) | <https://info.arxiv.org/help/policies/privacy_policy.html> |
| [Bibliotheksverbund Bayern](https://www.bib-bvb.de/) | <https://www.bib-bvb.de/web/guest/datenschutzerklaerung-bvb-homepage> |
| [Biodiversity Heritage Library](https://www.biodiversitylibrary.org/) | <https://www.si.edu/Privacy> |
| [Collection of Computer Science Bibliographies](https://en.wikipedia.org/wiki/Collection_of_Computer_Science_Bibliographies) | **currently unavailable**, offline |
| [CrossRef](https://www.crossref.org/) | <https://www.crossref.org/operations-and-sustainability/privacy/> |
| [dblp](https://dblp.uni-trier.de/) | <https://dblp.uni-trier.de/db/about/privacy.html> |
| [DJL (Deep Java Library)](https://djl.ai/) | <https://github.com/deepjavalibrary/djl/discussions/3370#discussioncomment-10233632> |
| [Directory of Open Access Books](https://www.doabooks.org/) | <https://www.doabooks.org/en/resources/accessibility> |
| [Digitala Vetenskapliga Arkivet](https://www.diva-portal.org/) | <https://www.uu.se/en/about-uu/data-protection-policy/> |
| [DOI Foundation](https://www.doi.org/) | <https://www.doi.org/privacy-policy/> |
| [Elsevier](https://www.elsevier.com/) | <https://www.elsevier.com/legal/privacy-policy> |
| [Google Scholar](https://scholar.google.com/) | <https://policies.google.com/privacy> |
| [Gemeinsamer Verbundkatalog](https://www.gbv.de/) | <https://www.gbv.de/datenschutz> |
| [Hugging Face](https://huggingface.co/) | <https://huggingface.co/privacy> |
| [IACR](https://www.iacr.org/) | <https://www.iacr.org/privacy.html> |
| [IEEEXplore](https://ieeexplore.ieee.org/Xplore/home.jsp) | <https://www.ieee.org/security-privacy.html> |
| [INSPIRE](https://inspirehep.net/) | <https://cern.service-now.com/service-portal?id=privacy_policy&se=INSPIRE-Online&notice=main> |
| [ISIDORE](https://isidore.science/) | <https://isidore.science/credit> |
| [JSTOR](https://www.jstor.org/) | <https://www.ithaka.org/privacypolicy/> |
| [Library of Congress](https://lccn.loc.gov/) | <https://www.loc.gov/legal/> |
| [Mistral AI](https://mistral.ai/) | <https://mistral.ai/terms/#privacy-policy> |
| [National Library of Medicine](https://www.ncbi.nlm.nih.gov/) | <https://www.nlm.nih.gov/web_policies.html> |
| [MathSciNet](http://www.ams.org/mathscinet) | <https://www.ams.org/about-us/privacy> |
| [mEDRA](https://www.medra.org/) | <https://www.medra.org/stdoc/en/Servizio_DOI_Informativa_ENG.pdf> |
| [Mr. DLib](https://mr-dlib.org/) [1] | <https://support.dataverse.harvard.edu/harvard-dataverse-privacy-policy> |
| [OpenAI](https://openai.com/) | <https://openai.com/policies/privacy-policy/> |
| [Openlibrary](https://openlibrary.org) | <https://archive.org/about/terms.php> |
| [ResearchGate](https://www.researchgate.net/) | <https://www.researchgate.net/privacy-policy> |
| [IETF Datatracker](https://datatracker.ietf.org/) | <https://www.ietf.org/privacy-statement/> |
| [Semantic Scholar](https://www.semanticscholar.org/), powered by [Allen Institute for AI](https://allenai.org/) | <https://allenai.org/privacy-policy> |
| [Springer Nature](https://dev.springernature.com/) | <https://dev.springernature.com/privacy-policy/> |
| [The SAO/NASA Astrophysics Data System](https://ui.adsabs.harvard.edu/) | <https://ui.adsabs.harvard.edu/help/privacy/> |
| [Unpaywall](https://unpaywall.org/) | <https://unpaywall.org/legal/privacy> |
| [zbMATH Open](https://www.zbmath.org) | <https://zbmath.org/privacy-policy/> |
| [ISIDORE](https://isidore.science/) | <https://isidore.science/credit> |
| [JSTOR](https://www.jstor.org/) | <https://www.ithaka.org/privacypolicy/> |
| [Library of Congress](https://lccn.loc.gov/) | <https://www.loc.gov/legal/> |
| [Mistral AI](https://mistral.ai/) | <https://mistral.ai/terms/#privacy-policy> |
| [National Library of Medicine](https://www.ncbi.nlm.nih.gov/) | <https://www.nlm.nih.gov/web_policies.html> |
| [MathSciNet](http://www.ams.org/mathscinet) | <https://www.ams.org/about-us/privacy> |
| [mEDRA](https://www.medra.org/) | <https://www.medra.org/stdoc/en/Servizio_DOI_Informativa_ENG.pdf> |
| [Mr. DLib](https://mr-dlib.org/) [1] | <https://support.dataverse.harvard.edu/harvard-dataverse-privacy-policy> |
| [OpenAI](https://openai.com/) | <https://openai.com/policies/privacy-policy/> |
| [Openlibrary](https://openlibrary.org) | <https://archive.org/about/terms.php> |
| [ResearchGate](https://www.researchgate.net/) | <https://www.researchgate.net/privacy-policy> |
| [IETF Datatracker](https://datatracker.ietf.org/) | <https://www.ietf.org/privacy-statement/> |
| [Semantic Scholar](https://www.semanticscholar.org/), powered by [Allen Institute for AI](https://allenai.org/) | <https://allenai.org/privacy-policy> |
| [Springer Nature](https://dev.springernature.com/) | <https://dev.springernature.com/privacy-policy/> |
| [The SAO/NASA Astrophysics Data System](https://ui.adsabs.harvard.edu/) | <https://ui.adsabs.harvard.edu/help/privacy/> |
| [Unpaywall](https://unpaywall.org/) | <https://unpaywall.org/legal/privacy> |
| [zbMATH Open](https://www.zbmath.org) | <https://zbmath.org/privacy-policy/> |
[1]: *Note: The Mr. DLib service is used for the related articles tab in the entry editor and collects also your language, your browser and operating system (*disabled* by default).*

@ -6,7 +6,7 @@ import org.jabref.build.xjc.XjcTask
plugins {
id 'application'
id 'com.github.andygoossens.modernizer' version '1.10.0'
id 'com.github.andygoossens.modernizer' version '1.9.3'
id 'me.champeau.jmh' version '0.7.2'
@ -15,7 +15,7 @@ plugins {
id 'org.openjfx.javafxplugin' version '0.1.0'
id 'org.beryx.jlink' version '3.1.0-rc-1'
id 'org.beryx.jlink' version '3.0.1'
// nicer test outputs during running and completion
// Homepage: https://github.com/radarsh/gradle-test-logger-plugin
@ -29,9 +29,7 @@ plugins {
id 'idea'
id 'org.openrewrite.rewrite' version '6.27.0'
id "org.itsallcode.openfasttrace" version "3.0.1"
id 'org.openrewrite.rewrite' version '6.20.0'
}
// Enable following for debugging
@ -45,8 +43,8 @@ group = "org.jabref"
version = project.findProperty('projVersion') ?: '100.0.0'
java {
sourceCompatibility = JavaVersion.VERSION_23
targetCompatibility = JavaVersion.VERSION_23
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
// Workaround needed for Eclipse, probably because of https://github.com/gradle/gradle/issues/16922
// Should be removed as soon as Gradle 7.0.1 is released ( https://github.com/gradle/gradle/issues/16922#issuecomment-828217060 )
@ -56,12 +54,8 @@ java {
// If this is updated, also update
// - .gitpod.Dockerfile
// - .devcontainer/devcontainer.json#L34 and
// - .github/workflows/deployment*.yml
// - .github/workflows/tests*.yml
// - .github/workflows/update-gradle-wrapper.yml
// - docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md
// - build.gradle -> jacoco -> toolVersion (because JaCoCo does not support newest JDK out of the box. Check versions at https://www.jacoco.org/jacoco/trunk/doc/changes.html)
languageVersion = JavaLanguageVersion.of(23)
// - .github/workflows/deployment-jdk-ea.yml#L53
languageVersion = JavaLanguageVersion.of(21)
// See https://docs.gradle.org/current/javadoc/org/gradle/jvm/toolchain/JvmVendorSpec.html for a full list
// vendor = JvmVendorSpec.AMAZON
}
@ -130,7 +124,6 @@ sourceSets {
repositories {
mavenCentral()
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
maven { url 'https://jitpack.io' }
maven { url 'https://oss.sonatype.org/content/groups/public' }
@ -147,12 +140,12 @@ dependencyLocking {
}
javafx {
version = "23.0.1"
version = "22.0.2"
modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.web', 'javafx.swing' ]
}
jacoco {
toolVersion = "0.8.13-SNAPSHOT"
toolVersion = "0.8.10"
}
dependencies {
@ -171,21 +164,21 @@ dependencies {
exclude group: 'commons-logging'
}
def luceneVersion = "10.0.0"
def luceneVersion = "9.11.1"
implementation "org.apache.lucene:lucene-core:$luceneVersion"
implementation "org.apache.lucene:lucene-queryparser:$luceneVersion"
implementation "org.apache.lucene:lucene-queries:$luceneVersion"
implementation "org.apache.lucene:lucene-analysis-common:$luceneVersion"
implementation "org.apache.lucene:lucene-highlighter:$luceneVersion"
implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.12.0'
implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.11.0'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.17.0'
implementation group: 'org.apache.commons', name: 'commons-text', version: '1.12.0'
implementation 'commons-logging:commons-logging:1.3.4'
implementation 'com.h2database:h2-mvstore:2.3.232'
// required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635
implementation 'org.bouncycastle:bcprov-jdk18on:1.79'
implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1'
implementation 'commons-cli:commons-cli:1.9.0'
@ -203,10 +196,10 @@ dependencies {
antlr4 'org.antlr:antlr4:4.13.2'
implementation 'org.antlr:antlr4-runtime:4.13.2'
implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '7.0.0.202409031743-r'
implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.10.0.202406032230-r'
implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.18.1'
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.18.1'
implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.17.2'
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.17.2'
implementation 'com.fasterxml:aalto-xml:1.3.3'
@ -215,8 +208,8 @@ dependencies {
implementation 'org.postgresql:postgresql:42.7.4'
// Support unix socket connection types
implementation 'com.kohlschutter.junixsocket:junixsocket-core:2.10.1'
implementation 'com.kohlschutter.junixsocket:junixsocket-mysql:2.10.1'
implementation 'com.kohlschutter.junixsocket:junixsocket-core:2.10.0'
implementation 'com.kohlschutter.junixsocket:junixsocket-mysql:2.10.0'
implementation ('com.oracle.ojdbc:ojdbc10:19.3.0.0') {
// causing module issues
@ -243,7 +236,7 @@ dependencies {
}
implementation 'org.fxmisc.flowless:flowless:0.7.3'
implementation 'org.fxmisc.richtext:richtextfx:0.11.3'
implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.64.0') {
implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.43.0') {
exclude module: 'javax.inject' // Split package, use only jakarta.inject
exclude module: 'commons-lang3'
exclude group: 'org.apache.commons.validator'
@ -256,19 +249,16 @@ dependencies {
}
// Required by gemsfx
implementation 'tech.units:indriya:2.2'
// Required by gemsfx and langchain4j
implementation ('com.squareup.retrofit2:retrofit:2.11.0') {
exclude group: 'com.squareup.okhttp3'
}
implementation 'org.controlsfx:controlsfx:11.2.1'
// region HTTP clients
implementation 'org.jsoup:jsoup:1.18.1'
implementation 'com.konghq:unirest-java-core:4.4.5'
implementation 'com.konghq:unirest-modules-gson:4.4.5'
implementation 'org.apache.httpcomponents.client5:httpclient5:5.4'
// endregion
implementation 'com.konghq:unirest-java-core:4.4.4'
implementation 'com.konghq:unirest-modules-gson:4.4.4'
implementation 'org.apache.httpcomponents.client5:httpclient5:5.3.1'
implementation 'org.slf4j:slf4j-api:2.0.16'
implementation 'org.tinylog:tinylog-api:2.7.0'
@ -276,9 +266,9 @@ dependencies {
implementation 'org.tinylog:tinylog-impl:2.7.0'
// route all requests to java.util.logging to SLF4J (which in turn routes to tinylog)
implementation 'org.slf4j:jul-to-slf4j:2.0.16'
implementation 'org.slf4j:jul-to-slf4j:2.0.13'
// route all requests to log4j to SLF4J
implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.24.1'
implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.23.1'
implementation('de.undercouch:citeproc-java:3.1.0') {
exclude group: 'org.antlr'
@ -307,17 +297,17 @@ dependencies {
// API
implementation 'jakarta.ws.rs:jakarta.ws.rs-api:4.0.0'
// Implementation of the API
implementation 'org.glassfish.jersey.core:jersey-server:3.1.9'
implementation 'org.glassfish.jersey.core:jersey-server:3.1.8'
// injection framework
implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.9'
implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.8'
implementation 'org.glassfish.hk2:hk2-api:3.1.1'
// testImplementation 'org.glassfish.hk2:hk2-testing:3.0.4'
// implementation 'org.glassfish.hk2:hk2-testing-jersey:3.0.4'
// testImplementation 'org.glassfish.hk2:hk2-junitrunner:3.0.4'
// HTTP server
// implementation 'org.glassfish.jersey.containers:jersey-container-netty-http:3.1.1'
implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.9'
testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.9'
implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.8'
testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.8'
// Allow objects "magically" to be mapped to JSON using GSON
// implementation 'org.glassfish.jersey.media:jersey-media-json-gson:3.1.1'
@ -331,58 +321,37 @@ dependencies {
implementation 'com.github.vatbub:mslinks:1.0.6.2'
// YAML formatting
implementation 'org.yaml:snakeyaml:2.3'
implementation 'org.yaml:snakeyaml:2.2'
// region AI
implementation 'dev.langchain4j:langchain4j:0.36.0'
// AI
implementation 'dev.langchain4j:langchain4j:0.33.0'
// Even though we use jvm-openai for LLM connection, we still need this package for tokenization.
implementation('dev.langchain4j:langchain4j-open-ai:0.36.0') {
exclude group: 'com.squareup.okhttp3'
exclude group: 'com.squareup.retrofit2', module: 'retrofit'
exclude group: 'org.jetbrains.kotlin'
implementation('dev.langchain4j:langchain4j-open-ai:0.33.0') {
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
}
implementation('dev.langchain4j:langchain4j-mistral-ai:0.36.0') {
exclude group: 'com.squareup.okhttp3'
exclude group: 'com.squareup.retrofit2', module: 'retrofit'
exclude group: 'org.jetbrains.kotlin'
}
implementation('dev.langchain4j:langchain4j-google-ai-gemini:0.36.0') {
exclude group: 'com.squareup.okhttp3'
exclude group: 'com.squareup.retrofit2', module: 'retrofit'
}
implementation('dev.langchain4j:langchain4j-hugging-face:0.36.0') {
exclude group: 'com.squareup.okhttp3'
exclude group: 'com.squareup.retrofit2', module: 'retrofit'
exclude group: 'org.jetbrains.kotlin'
}
implementation 'org.apache.velocity:velocity-engine-core:2.4.1'
implementation platform('ai.djl:bom:0.30.0')
implementation 'ai.djl:api'
implementation 'ai.djl.huggingface:tokenizers'
implementation 'ai.djl.pytorch:pytorch-model-zoo'
implementation 'io.github.stefanbratanov:jvm-openai:0.11.0'
implementation('dev.langchain4j:langchain4j-mistral-ai:0.33.0')
implementation('dev.langchain4j:langchain4j-hugging-face:0.33.0')
implementation 'ai.djl:api:0.29.0'
implementation 'ai.djl.pytorch:pytorch-model-zoo:0.29.0'
implementation 'ai.djl.huggingface:tokenizers:0.29.0'
implementation 'io.github.stefanbratanov:jvm-openai:0.10.0'
// openai depends on okhttp, which needs kotlin - see https://github.com/square/okhttp/issues/5299 for details
implementation ('com.squareup.okhttp3:okhttp:4.12.0') {
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
}
// GemxFX also (transitively) depends on kotlin
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21'
// endregion
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.20'
implementation 'commons-io:commons-io:2.17.0'
implementation 'commons-io:commons-io:2.16.1'
// Even if "compileOnly" is used, IntelliJ always adds to module-info.java. To avoid issues during committing, we use "implementation" instead of "compileOnly"
implementation 'io.github.adr:e-adr:2.0.0-SNAPSHOT'
implementation 'io.zonky.test:embedded-postgres:2.0.7'
implementation enforcedPlatform('io.zonky.test.postgres:embedded-postgres-binaries-bom:17.0.0')
testImplementation 'io.github.classgraph:classgraph:4.8.177'
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3'
testImplementation 'io.github.classgraph:classgraph:4.8.175'
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'
testImplementation 'org.junit.platform:junit-platform-launcher:1.10.3'
testImplementation 'org.mockito:mockito-core:5.14.2'
testImplementation 'org.mockito:mockito-core:5.13.0'
testImplementation 'org.xmlunit:xmlunit-core:2.10.0'
testImplementation 'org.xmlunit:xmlunit-matchers:2.10.0'
testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.3.0'
@ -390,17 +359,13 @@ dependencies {
testImplementation "org.testfx:testfx-core:4.0.16-alpha"
testImplementation "org.testfx:testfx-junit5:4.0.16-alpha"
testImplementation "org.hamcrest:hamcrest-library:3.0"
testImplementation "com.github.javaparser:javaparser-symbol-solver-core:3.26.2"
// recommended by https://github.com/wiremock/wiremock/issues/2149#issuecomment-1835775954
testImplementation 'org.wiremock:wiremock-standalone:3.9.2'
checkstyle 'com.puppycrawl.tools:checkstyle:10.20.1'
checkstyle 'com.puppycrawl.tools:checkstyle:10.18.0'
// xjc needs the runtime as well for the ant task, otherwise it fails
xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2'
xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2'
rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.19.0"))
rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.17.0"))
rewrite("org.openrewrite.recipe:rewrite-static-analysis")
rewrite("org.openrewrite.recipe:rewrite-logging-frameworks")
rewrite("org.openrewrite.recipe:rewrite-testing-frameworks")
@ -765,14 +730,16 @@ jlink {
// 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.
// This is based on ./gradlew suggestMergedModuleInfo, sort, strip ";"", remove non-used modules, and include the suggested directives here.
// This is based on ./gradlew suggestMergedModuleInfo, sort, strip ";"", comment non-used modules, and include the suggested directives here.
// However, we need to fine-tune this manually (non-alphabetic order)
mergedModule {
requires 'com.google.gson'
requires 'com.fasterxml.jackson.annotation'
requires 'com.fasterxml.jackson.databind'
requires 'com.fasterxml.jackson.core'
requires 'com.fasterxml.jackson.datatype.jdk8'
requires 'jakarta.xml.bind'
requires 'javafx.base'
requires 'javafx.controls'
requires 'javafx.fxml'
requires 'javafx.graphics'
requires 'javafx.media'
requires 'javafx.swing'
requires 'java.compiler'
requires 'java.datatransfer'
requires 'java.desktop'
@ -780,48 +747,33 @@ jlink {
requires 'java.management'
requires 'java.naming'
requires 'java.net.http'
requires 'java.rmi'
requires 'java.scripting'
requires 'java.security.jgss'
requires 'java.security.sasl'
requires 'java.sql'
requires 'java.sql.rowset'
requires 'java.transaction.xa'
requires 'java.rmi'
requires 'java.xml'
requires 'javafx.base'
requires 'javafx.controls'
requires 'javafx.fxml'
requires 'javafx.graphics'
requires 'javafx.media'
requires 'javafx.swing'
requires 'jdk.jsobject'
requires 'jdk.security.jgss'
requires 'jdk.unsupported'
requires 'jdk.unsupported.desktop'
requires 'jdk.security.jgss'
requires 'jdk.xml.dom'
requires 'org.apache.commons.lang3'
requires 'org.apache.commons.logging'
requires 'org.apache.commons.text'
requires 'org.apache.commons.codec'
requires 'org.apache.commons.io'
requires 'org.apache.commons.compress'
requires 'org.freedesktop.dbus'
requires 'com.google.gson'
requires 'org.jsoup'
requires 'org.slf4j'
requires 'org.tukaani.xz';
uses 'ai.djl.engine.EngineProvider'
uses 'ai.djl.repository.RepositoryFactory'
uses 'ai.djl.repository.zoo.ZooProvider'
uses 'dev.langchain4j.spi.prompt.PromptTemplateFactory'
uses 'kong.unirest.core.json.JsonEngine'
uses 'org.eclipse.jgit.lib.Signer'
uses 'org.eclipse.jgit.transport.SshSessionFactory'
uses 'org.mariadb.jdbc.LocalInfileInterceptor'
uses 'org.mariadb.jdbc.authentication.AuthenticationPlugin'
requires 'jakarta.xml.bind'
requires 'org.apache.commons.lang3'
requires 'org.apache.commons.text'
requires 'org.apache.commons.logging';
uses 'org.mariadb.jdbc.credential.CredentialPlugin'
uses 'org.mariadb.jdbc.authentication.AuthenticationPlugin'
uses 'org.mariadb.jdbc.tls.TlsSocketPlugin'
uses 'org.postgresql.shaded.com.ongres.stringprep.Profile'
uses 'org.mariadb.jdbc.LocalInfileInterceptor'
uses 'org.eclipse.jgit.transport.SshSessionFactory'
uses 'org.eclipse.jgit.lib.GpgSigner'
uses 'kong.unirest.core.json.JsonEngine';
provides 'org.mariadb.jdbc.tls.TlsSocketPlugin' with 'org.mariadb.jdbc.internal.protocol.tls.DefaultTlsSocketPlugin'
provides 'java.sql.Driver' with 'org.postgresql.Driver'
provides 'org.mariadb.jdbc.authentication.AuthenticationPlugin' with 'org.mariadb.jdbc.internal.com.send.authentication.CachingSha2PasswordPlugin',
@ -838,12 +790,7 @@ jlink {
provides 'java.security.Provider' with 'org.bouncycastle.jce.provider.BouncyCastleProvider',
'org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider'
provides 'kong.unirest.core.json.JsonEngine' with 'kong.unirest.modules.gson.GsonEngine';
provides 'ai.djl.repository.zoo.ZooProvider' with 'ai.djl.engine.rust.zoo.RsZooProvider',
'ai.djl.huggingface.zoo.HfZooProvider',
'ai.djl.pytorch.zoo.PtZooProvider',
'ai.djl.repository.zoo.DefaultZooProvider';
provides 'ai.djl.engine.EngineProvider' with 'ai.djl.engine.rust.RsEngineProvider',
'ai.djl.pytorch.engine.PtEngineProvider';
}
jpackage {
@ -949,7 +896,3 @@ jmh {
iterations = 10
fork = 2
}
requirementTracing {
inputDirectories = files('docs', 'src/main/java', 'src/test/java')
}

@ -8,19 +8,18 @@ The project is versioned using [CalVer](https://calver.org/).
### Added
- Added checker "Escaped Ampersands": `check_ampersands.py` which checks all CSV journals in the folder `journals` to make
sure all instances of ampersands are unescaped
- Added checker "Escaped Ampersands": `check_ampersands.py` which checks all CSV journals in the folder `journals` to make sure all instances of ampersands are unescaped
### Changed
- `.github/workflows/tests.yml` contains the script `check_ampersands.py`
- Minor format changes in `README.md` and `LISENSE.md` as the old GitHub actions check was already failing
- Found an escaped ampersands using the new script in `journal_abbreviations_dainst.csv` so this was amended.
- Found an escaped ampersand using the new script in `journal_abbreviations_dainst.csv` so this was amended.
### Removed
- `[;<frequency>]` was removed, because it was used very seldom - and the data should be collected at other places.
- "Web of Science" abbreviation list was removed. The [data source](https://su.figshare.com/articles/dataset/Journal_abbreviations_from_Web_of_Science/3207787) is from 2016 and had serious issues. [#176](https://github.com/JabRef/abbrv.jabref.org/issues/176)
## 2021-09

@ -13,8 +13,6 @@
| Geology and Physics | [`journal_abbreviations_geology_physics.csv`](journal_abbreviations_geology_physics.csv) | [anonymous user](https://sourceforge.net/p/jabref/patches/164/) and Jonas Lähnemann. | |
| IEEE | [`journal_abbreviations_ieee.csv`](journal_abbreviations_ieee.csv) | Thomas Arildsen and “eyliu” | |
| Index Medicus | [`journal_abbreviations_medicus.csv`](journal_abbreviations_medicus.csv) | Guy Tsafnat | Provides Medline (dotless) abbreviations only. |
| ISI Web of Science (with dots) | [`journal_abbreviations_webofscience-dots.csv`](journal_abbreviations_webofscience-dots.csv) | Alistair Auffret | |
| ISI Web of Science (dotless) | [`journal_abbreviations_webofscience-dotless.csv`](journal_abbreviations_webofscience.csv) | Alistair Auffret | |
| Life Science | [`journal_abbreviations_lifescience.csv`](journal_abbreviations_lifescience.csv) | Zé Roberto Ribeiro | |
| Mathematics | [`journal_abbreviations_mathematics.csv`](journal_abbreviations_mathematics.csv) | | From [MathSciNet](https://mathscinet.ams.org/mathscinet/help/librarians.html) (look for "(CSV file)"), generated by [`update_mathscinet.py`](../scripts/update_mathscinet.py) |
| Mechanical and biomechanical | [`journal_abbreviations_mechanical.csv`](journal_abbreviations_mechanical.csv) | [anonymous user](https://sourceforge.net/p/jabref/patches/151/) | |

@ -335,6 +335,7 @@
"Applications of Mathematics","Appl. Math."
"Applications of Modern Technology in Business","Appl. Modern Tech. Business"
"Applied Analysis and Optimization","Appl. Anal. Optim."
"Applied Categorical Structures. A Journal Devoted to Applications of Categorical Methods in Algebra, Analysis, Computer Science, Logic, Order and Topology","Appl. Categ. Structures"
"Applied Categorical Structures. A Journal Devoted to Applications of Categorical Methods in Algebra, Analysis, Order, Topology and Computer Science","Appl. Categ. Structures"
"Applied Computer Science. Berichte zur Praktischen Informatik","Appl. Comput. Sci."
"Applied General Topology","Appl. Gen. Topol."
@ -780,6 +781,7 @@
"Computational Geosciences","Comput. Geosci."
"Computational Geosciences. Modeling, Simulation and Data Analysis","Comput. Geosci."
"Computational Imaging and Vision","Comput. Imaging Vision"
"Computational Intelligence Methods and Applications","Comput. Intell. Methods Appl."
"Computational Intelligence. An International Journal","Comput. Intell."
"Computational Linguistics","Comput. Linguist."
"Computational Management Science","Comput. Manag. Sci."
@ -1053,6 +1055,7 @@
"Electronics Texts for Engineers and Scientists","Electron. Texts Engrs. Sci."
"Elemente der Mathematik","Elem. Math."
"Elements in Applied Category Theory","Elem. Appl. Categ. Theory"
"Elements in Decision Theory and Philosophy","Elem. Decis. Theory Philos."
"Elements in Non-local Data Interactions: Foundations and Applications","Elem. Non-local Data Interact.: Found. Appl."
"Elements in the Philosophy of Biology","Elem. Philos. Biol."
"Elements in the Philosophy of Mathematics","Elem. Philos. Math."
@ -1332,6 +1335,7 @@
"IAS/Park City Mathematics Series","IAS/Park City Math. Ser."
"IAU Symposium","IAU Symp."
"ICCM Notices. Notices of the International Congress of Chinese Mathematicians","ICCM Not."
"ICCM Notices. Notices of the International Consortium of Chinese Mathematicians","ICCM Not."
"ICIAM 2019 SEMA SIMAI Springer Series","ICIAM 2019 SEMA SIMAI Springer Ser."
"ICM Series","ICM Ser."
"ICME-13 Monographs","ICME-13 Monogr."
@ -2092,6 +2096,7 @@
"London Mathematical Society Lecture Note Series","London Math. Soc. Lecture Note Ser."
"London Mathematical Society Student Texts","London Math. Soc. Stud. Texts"
"London Mathematical Society. Newsletter","Lond. Math. Soc. Newsl."
"Lumiere Classique","Lumiere Class."
"MAA Problem Books Series","MAA Probl. Books Ser."
"MAA Textbooks","MAA Textb."
"MAT. Serie A: Conferencias, Seminarios y Trabajos de Matematica","MAT Ser. A Conf. Semin. Trab. Mat."
@ -2143,6 +2148,7 @@
"Materials Science Series","Mater. Sci. Ser."
"Math Horizons","Math Horiz."
"Math-for-Industry (MI) Lecture Note Series","Math-for-Ind. (MI) Lect. Note Ser."
"Mathematic Study Resources","Math. Study Resour."
"MathematicS in Action","MathS in Action"
"Mathematica Applicanda. Matematyka Stosowana","Math. Appl. (Warsaw)"
"Mathematica Applicata. Yingyong Shuxue","Math. Appl. (Wuhan)"
@ -2299,6 +2305,7 @@
"Memoirs of the Institute of Science and Engineering. Ritsumeikan University","Mem. Inst. Sci. Engrg. Ritsumeikan Univ."
"Memoirs on Differential Equations and Mathematical Physics","Mem. Differ. Equ. Math. Phys."
"Memorias de la Sociedad Matematica Mexicana","Mem. Soc. Mat. Mex."
"Mens Agitat---Colloq.","Mens Agit.---Colloquia"
"Meridian: Crossing Aesthetics","Meridian Crossing Aesthet."
"Metalogicon. International Journal of Pure and Applied Logic, Linguistics and Philosophy. New Series","Metalogicon (N.S.)"
"Methoden der Psychologie","Methoden Psych."
@ -3216,6 +3223,7 @@
"Springers Kurzlehrbucher der Wirtschaftswissenschaften","Springers Kurzlehrb. Wirtsch."
"St. Petersburg Mathematical Journal","St. Petersburg Math. J."
"Stability, Oscillations and Optimization of Systems","Stab. Oscil. Optim. Syst."
"Stanford Text Technologies","Stanf. Text Technol."
"State of the Art Reports","State Art Rep."
"Static & Dynamic Game Theory: Foundations & Applications","Static Dyn. Game Theory Found. Appl."
"Statistica Neerlandica. Journal of the Netherlands Society for Statistics and Operations Research","Stat. Neerl."
@ -3633,11 +3641,13 @@
"Vychislitelnaya Tekhnika Sotsialisticheskikh Stran","Vychisl. Tekhn. Sots. Stran"
"Vychislitelnye Tekhnologii. Computational Technologies","Vychisl. Tekhnol."
"Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie Sistemi,","Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie"
"Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie Sistemy","Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie"
"Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie Sistemy,","Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie"
"WIT Transactions on Engineering Sciences","WIT Trans. Eng. Sci."
"WIT Transactions on Modelling and Simulation","WIT Trans. Model. Simul."
"Wakayama University. Faculty of Education. Bulletin. Natural Science","Bull. Fac. Ed. Wakayama Univ. Natur. Sci."
"Walter Eucken Institut. Wirtschaftswissenschaftliche und Wirtschaftsrechtliche Untersuchungen","Walter Eucken Inst. Wirtschaftswissensch. Wirtschaftsrechtl. Unters."
"Warburg Institute Studies and Texts","Warburg Inst. Stud. Texts"
"Water Waves. An Interdisciplinary Journal","Water Waves"
"Wave Motion. An International Journal Reporting Research on Wave Phenomena","Wave Motion"
"Waves in Random and Complex Media. Propagation, Scattering and Imaging","Waves Random Complex Media"

1 30$\\sp {\\rm o}$ Coloquio Brasileiro de Matematica 30$\\sp {\\rm o}$ Coloq. Bras. Mat.
335 Applications of Mathematics Appl. Math.
336 Applications of Modern Technology in Business Appl. Modern Tech. Business
337 Applied Analysis and Optimization Appl. Anal. Optim.
338 Applied Categorical Structures. A Journal Devoted to Applications of Categorical Methods in Algebra, Analysis, Computer Science, Logic, Order and Topology Appl. Categ. Structures
339 Applied Categorical Structures. A Journal Devoted to Applications of Categorical Methods in Algebra, Analysis, Order, Topology and Computer Science Appl. Categ. Structures
340 Applied Computer Science. Berichte zur Praktischen Informatik Appl. Comput. Sci.
341 Applied General Topology Appl. Gen. Topol.
781 Computational Geosciences Comput. Geosci.
782 Computational Geosciences. Modeling, Simulation and Data Analysis Comput. Geosci.
783 Computational Imaging and Vision Comput. Imaging Vision
784 Computational Intelligence Methods and Applications Comput. Intell. Methods Appl.
785 Computational Intelligence. An International Journal Comput. Intell.
786 Computational Linguistics Comput. Linguist.
787 Computational Management Science Comput. Manag. Sci.
1055 Electronics Texts for Engineers and Scientists Electron. Texts Engrs. Sci.
1056 Elemente der Mathematik Elem. Math.
1057 Elements in Applied Category Theory Elem. Appl. Categ. Theory
1058 Elements in Decision Theory and Philosophy Elem. Decis. Theory Philos.
1059 Elements in Non-local Data Interactions: Foundations and Applications Elem. Non-local Data Interact.: Found. Appl.
1060 Elements in the Philosophy of Biology Elem. Philos. Biol.
1061 Elements in the Philosophy of Mathematics Elem. Philos. Math.
1335 IAS/Park City Mathematics Series IAS/Park City Math. Ser.
1336 IAU Symposium IAU Symp.
1337 ICCM Notices. Notices of the International Congress of Chinese Mathematicians ICCM Not.
1338 ICCM Notices. Notices of the International Consortium of Chinese Mathematicians ICCM Not.
1339 ICIAM 2019 SEMA SIMAI Springer Series ICIAM 2019 SEMA SIMAI Springer Ser.
1340 ICM Series ICM Ser.
1341 ICME-13 Monographs ICME-13 Monogr.
2096 London Mathematical Society Lecture Note Series London Math. Soc. Lecture Note Ser.
2097 London Mathematical Society Student Texts London Math. Soc. Stud. Texts
2098 London Mathematical Society. Newsletter Lond. Math. Soc. Newsl.
2099 Lumiere Classique Lumiere Class.
2100 MAA Problem Books Series MAA Probl. Books Ser.
2101 MAA Textbooks MAA Textb.
2102 MAT. Serie A: Conferencias, Seminarios y Trabajos de Matematica MAT Ser. A Conf. Semin. Trab. Mat.
2148 Materials Science Series Mater. Sci. Ser.
2149 Math Horizons Math Horiz.
2150 Math-for-Industry (MI) Lecture Note Series Math-for-Ind. (MI) Lect. Note Ser.
2151 Mathematic Study Resources Math. Study Resour.
2152 MathematicS in Action MathS in Action
2153 Mathematica Applicanda. Matematyka Stosowana Math. Appl. (Warsaw)
2154 Mathematica Applicata. Yingyong Shuxue Math. Appl. (Wuhan)
2305 Memoirs of the Institute of Science and Engineering. Ritsumeikan University Mem. Inst. Sci. Engrg. Ritsumeikan Univ.
2306 Memoirs on Differential Equations and Mathematical Physics Mem. Differ. Equ. Math. Phys.
2307 Memorias de la Sociedad Matematica Mexicana Mem. Soc. Mat. Mex.
2308 Mens Agitat---Colloq. Mens Agit.---Colloquia
2309 Meridian: Crossing Aesthetics Meridian Crossing Aesthet.
2310 Metalogicon. International Journal of Pure and Applied Logic, Linguistics and Philosophy. New Series Metalogicon (N.S.)
2311 Methoden der Psychologie Methoden Psych.
3223 Springers Kurzlehrbucher der Wirtschaftswissenschaften Springers Kurzlehrb. Wirtsch.
3224 St. Petersburg Mathematical Journal St. Petersburg Math. J.
3225 Stability, Oscillations and Optimization of Systems Stab. Oscil. Optim. Syst.
3226 Stanford Text Technologies Stanf. Text Technol.
3227 State of the Art Reports State Art Rep.
3228 Static & Dynamic Game Theory: Foundations & Applications Static Dyn. Game Theory Found. Appl.
3229 Statistica Neerlandica. Journal of the Netherlands Society for Statistics and Operations Research Stat. Neerl.
3641 Vychislitelnaya Tekhnika Sotsialisticheskikh Stran Vychisl. Tekhn. Sots. Stran
3642 Vychislitelnye Tekhnologii. Computational Technologies Vychisl. Tekhnol.
3643 Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie Sistemi, Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie
3644 Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie Sistemy Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie
3645 Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie Sistemy, Vychislitelnyi Tsentr Moskovskogo Gosudarstvennogo Universiteta. Seriya: Statistika i Stokhasticheskie
3646 WIT Transactions on Engineering Sciences WIT Trans. Eng. Sci.
3647 WIT Transactions on Modelling and Simulation WIT Trans. Model. Simul.
3648 Wakayama University. Faculty of Education. Bulletin. Natural Science Bull. Fac. Ed. Wakayama Univ. Natur. Sci.
3649 Walter Eucken Institut. Wirtschaftswissenschaftliche und Wirtschaftsrechtliche Untersuchungen Walter Eucken Inst. Wirtschaftswissensch. Wirtschaftsrechtl. Unters.
3650 Warburg Institute Studies and Texts Warburg Inst. Stud. Texts
3651 Water Waves. An Interdisciplinary Journal Water Waves
3652 Wave Motion. An International Journal Reporting Research on Wave Phenomena Wave Motion
3653 Waves in Random and Complex Media. Propagation, Scattering and Imaging Waves Random Complex Media

@ -1305,6 +1305,7 @@
"Asia Pacific Journal Of Molecular Biology And Biotechnology","Asia Pac. J. Mol. Biol. Biotechnol."
"Asia Pacific Journal of Medical Toxicology","Asia Pac. J. Med. Toxicol."
"Asia Pacific Mathematics Newsletters","Asia Pac. Math. Newsl."
"Asia Pacific Physics Newsletter","Asia Pac. Phys. Newsl."
"Asia-Pacific Biotech News","Asia-Pac. Biotech News"
"Asia-Pacific Journal of Atmospheric Sciences","Asia-Pac. J. Atmos. Sci."
"Asia-Pacific Journal of Chemical Engineering","Asia-Pac. J. Chem. Eng."
@ -2956,6 +2957,7 @@
"Condensed Matter","Condens. Matter"
"Condensed Matter Physics","Condens. Matter Phys."
"Condensed Matter Theories","Condens. Matter Theor."
"Confluentes Mathematici","Confluentes Math."
"Connection Science","Connect. Sci."
"Connective Tissue Research","Connect. Tissue Res."
"Conservation Biology","Conserv. Biol."
@ -3228,6 +3230,7 @@
"Data and Knowledge Engineering","Data Knowl. Eng."
"Data in Brief","Data Brief"
"Data-Centric Engineering","Data-Centric Eng."
"Data-Driven Modelling","Data-Driven Modell."
"Decision Sciences","Decis. Sci."
"Decision Support Systems","Decis. Support Syst."
"Deep Sea Research Part A: Oceanographic Research Papers","Deep Sea Res. Part A"
@ -3649,6 +3652,7 @@
"Energy Reports","Energy Rep."
"Energy Research & Social Science","Energy Res. Social Sci."
"Energy Reviews","Energy Rev."
"Energy Science","Energy Sci."
"Energy Science & Engineering","Energy Sci. Eng."
"Energy Sources, Part A: Recovery, Utilization, and Environmental Effects","Energy Sources Part A"
"Energy Sources, Part B: Economics, Planning and Policy","Energy Sources Part B"
@ -3728,6 +3732,7 @@
"Environment and Planning F","Environ. Plann. F"
"Environment and Security","Environ. Secur."
"Environment, Development and Sustainability","Environ. Dev. Sustainability"
"Environment, Innovation and Management","Environ. Innovation Manage."
"Environment: Science and Policy for Sustainable Development","Environ.: Sci. Policy Sustainable Dev."
"Environmental Advances","Environ. Adv."
"Environmental Analysis Health and Toxicology","Environ. Anal. Health Toxicol."
@ -4701,6 +4706,7 @@
"Geomechanics and Tunnelling","Geomech. Tunnelling"
"Geomechanics for Energy and the Environment","Geomech. Energy Environ."
"Geometriae Dedicata","Geom. Dedicata"
"Geometric Mechanics","Geom. Mech."
"Geometric and Functional Analysis","Geom. Funct. Anal."
"Geometry & Topology","Geom. Topol"
"Geometry, Imaging and Computing","Geom. Imaging Comput."
@ -5634,6 +5640,7 @@
"Interacting with Computers","Interact. Comput."
"Interceram - International Ceramic Review","Interceram - Int. Ceram. Rev."
"Interdisciplinary Environmental Review","Interdiscip. Environ. Rev."
"Interdisciplinary Journal of Sustainable Oceans and Coasts","Interdiscip. J. Sustainable Oceans Coasts"
"Interdisciplinary Materials","Interdiscip. Mater."
"Interdisciplinary Neurosurgery: Advanced Techniques and Case Management","Interdiscip. Neurosurg. Adv. Tech. Case Manage."
"Interdisciplinary Science Reviews","Interdiscip. Sci. Rev."
@ -5807,6 +5814,7 @@
"International Journal of Cast Metals Research","Int. J. Cast Met. Res."
"International Journal of Cell Biology","Int. J. Cell Biol."
"International Journal of Cell Cloning","Int. J. Cell Cloning"
"International Journal of Cell and Tissue Engineering, Artificial Cells and Regenerative Medicine","Int. J. Cell Tissue Eng. Artif. Cells Regener. Med."
"International Journal of Cement Composites and Lightweight Concrete","Int. J. Cem. Compos. Lightweight Concr."
"International Journal of Ceramic Engineering and Science","Int. J. Ceram. Eng. Sci."
"International Journal of Chemical Engineering","Int. J. Chem. Eng."
@ -6410,7 +6418,6 @@
"International Journal of Web and Grid Services","Int. J. Web Grid Serv."
"International Journal of Wildland Fire","Int. J. Wildland Fire"
"International Journal of Wine Research","Int. J. Wine Res."
"International Journal of Wireless & Optical Communications","Int. J. Wireless Opt. Commun."
"International Journal of Wireless Information Networks","Int. J. Wireless Inf. Networks"
"International Journal of Wireless and Mobile Computing","Int. J. Wireless Mobile Comput."
"International Journal of Wrestling Science","Int. J. Wrestling Sci."
@ -6428,6 +6435,7 @@
"International Journal on Smart and Sustainable Cities","Int. J. Smart Sustainable Cities"
"International Journal on Software Tools for Technology Transfer","Int. J. Software Tools Technol. Transfer"
"International Journal on Uncertainty Quantification","Int. J. Uncertainty Quantif."
"International Journal on Wireless & Optical Communications","Int. J. Wireless Opt. Commun."
"International Marine Energy Journal","Int. Mar. Energy J."
"International Materials Reviews","Int. Mater. Rev."
"International Mathematics Research Notices","Int. Math. Res. Not."
@ -7171,6 +7179,7 @@
"Journal of Construction Engineering","J. Constr. Eng."
"Journal of Construction Engineering Management","J. Constr. Eng. Manage."
"Journal of Construction Engineering and Management","J. Constr. Eng. Manage."
"Journal of Construction Research","J. Constr. Res."
"Journal of Constructional Steel Research","J. Constr. Steel Res."
"Journal of Consumer Protection and Food Safety","J. Consum. Prot, Food Saf."
"Journal of Contaminant Hydrology","J. Contam. Hydrol."
@ -7200,6 +7209,7 @@
"Journal of Cybersecurity","J. Cybersecur."
"Journal of Dairy Research","J. Dairy Res."
"Journal of Dairy Science","J. Dairy Sci."
"Journal of Data and Dynamic Systems","J. Data Dyn. Syst."
"Journal of Data and Information Quality","J. Data Inf. Qual."
"Journal of Data, Information and Management","J. Data Inf. Manage."
"Journal of Decision Systems","J. Decis. Syst."
@ -7324,6 +7334,7 @@
"Journal of Environmental Management","J. Environ. Manage."
"Journal of Environmental Materials and Sustainable Energy","J. Environ. Mater. Sustainable Energy"
"Journal of Environmental Monitoring","J. Environ. Monit."
"Journal of Environmental Nanotechnology","J. Environ. Nanotechnol."
"Journal of Environmental Pathology and Toxicology","J. Environ. Pathol. Toxicol."
"Journal of Environmental Pathology, Toxicology and Oncology","J. Environ. Pathol. Toxicol. Oncol."
"Journal of Environmental Planning and Management","J. Environ. Plann. Manage."
@ -8425,6 +8436,7 @@
"Journal of Sustainable Development of Energy, Water and Environment Systems","J. Sustainable Dev. Energy Water Environ. Syst."
"Journal of Sustainable Energy Engineering","J. Sustainable Energy Eng."
"Journal of Sustainable Forestry","J. Sustainable For."
"Journal of Sustainable Materials Science and Engineering","J. Sustainable Mater. Sci. Eng."
"Journal of Sustainable Metallurgy","J. Sustainable Metall."
"Journal of Sustainable Mining","J. Sustainable Min."
"Journal of Sustainable Product Design","J. Sustainable Prod. Des."
@ -9127,6 +9139,7 @@
"Materials Express","Mater. Express"
"Materials Forum","Mater. Forum"
"Materials Horizons","Mater. Horiz."
"Materials Informatics and Data Science","Mater. Inf. Data Sci."
"Materials Letters","Mater. Lett."
"Materials Letters: X","Mater. Lett.: X"
"Materials Open","Mater. Open"
@ -9173,6 +9186,7 @@
"Materials World","Mater. World"
"Materials and Corrosion","Mater. Corros."
"Materials and Design","Mater. Des."
"Materials and Emerging Technologies for Sustainability","Mater. Emerging Technol. Sustainability"
"Materials and Manufacturing Processes","Mater. Manuf. Processes"
"Materials and Organisms","Mater. Org."
"Materials and Structures","Mater. Struct."
@ -9677,6 +9691,7 @@
"Nano Letters","Nano Lett."
"Nano Materials Science","Nano Mater. Sci."
"Nano Micro Letters","Nano Micro Lett."
"Nano Micro Mechanics Review","Nano Micro Mech. Rev."
"Nano Research","Nano Res."
"Nano Reviews & Experiments","Nano Rev. Exp."
"Nano Select","Nano Select"
@ -9723,6 +9738,7 @@
"Natural Hazards and Earth System Sciences","Nat. Hazards Earth Syst. Sci."
"Natural Hazards and Earth System Sciences Discussions","Nat. Hazards Earth Syst. Sci. Discuss."
"Natural History","Nat. Hist."
"Natural History Collections and Museomics","Nat. Hist. Collect. Museomics"
"Natural Language Engineering","Nat. Lang. Eng."
"Natural Language Processing Journal","Nat. Lang. Process. J."
"Natural Product Communications","Nat. Prod. Commun."
@ -12721,9 +12737,11 @@
"World Review of Science Technology and Sustainable Development","World Rev. Sci. Technol. Sustainable Dev."
"World Scientific Annual Review of Artificial Intelligence","World Sci. Annu. Rev. Artif. Intell."
"World Scientific Annual Review of Biomechanics","World Sci. Annu. Rev. Biomech."
"World Scientific Annual Review of Cancer Immunology","World Sci. Annu. Rev. Cancer Immunol."
"World Scientific Annual Review of Data Science","World Sci. Annu. Rev. Data Sci."
"World Scientific Annual Review of FinTech","World Sci. Annu. Rev. FinTech"
"World Scientific Annual Review of Functional Materials","World Sci. Annu. Rev. Funct. Mate."
"World Scientific Annual Review of Particle Physics","World Sci. Annu. Rev. Part. Phys."
"World Scientific Annual Review of Vaccine Design","World Sci. Annu. Rev. Vaccine Des."
"World Water Policy","World Water Policy"
"World Wide Web","World Wide Web"
@ -12764,6 +12782,7 @@
"Zeitschrift fur Elektrotechnik und Elektrochemie","Z. Elektrotech. Elektrochem."
"Zeitschrift fur Erzbergbau und Metallhuttenwesen","Z. Erzbergbau MetallhuttenWes."
"Zeitschrift fur Experimentelle Pathologie und Therapie","Z. Exp. Pathol. Ther."
"Zeitschrift fur Flugtechnik und Motorluftschiffahrt","Z. Flugtech. Motorluftschiffahrt"
"Zeitschrift fur Flugwissenschaften und Weltraumforschung","Z. Flugwiss. Weltraumforsch."
"Zeitschrift fur Geologische Wissenschaften","Z. Geol. Wiss."
"Zeitschrift fur Geomorphologie","Z. Geomorphol."

Can't render this file because it is too large.

@ -22,8 +22,7 @@ import sys
# Define the list of CSV files
import_order = [
"journals/journal_abbreviations_entrez.csv",
"journals/journal_abbreviations_medicus.csv",
"journals/journal_abbreviations_webofscience-dotless.csv",
"journals/journal_abbreviations_medicus.csv"
]

@ -32,8 +32,7 @@ import_order = [
"journals/journal_abbreviations_mathematics.csv",
"journals/journal_abbreviations_mechanical.csv",
"journals/journal_abbreviations_meteorology.csv",
"journals/journal_abbreviations_sociology.csv",
"journals/journal_abbreviations_webofscience-dots.csv",
"journals/journal_abbreviations_sociology.csv"
]

@ -32,10 +32,8 @@ import_order = [
'journals/journal_abbreviations_mechanical.csv',
'journals/journal_abbreviations_meteorology.csv',
'journals/journal_abbreviations_sociology.csv',
'journals/journal_abbreviations_webofscience-dots.csv',
'journals/journal_abbreviations_entrez.csv',
'journals/journal_abbreviations_medicus.csv',
'journals/journal_abbreviations_webofscience-dotless.csv',
'journals/journal_abbreviations_aea.csv',
'journals/journal_abbreviations_annee-philologique.csv',
'journals/journal_abbreviations_astronomy.csv',

@ -17,9 +17,7 @@ import_order = [
'../journals/journal_abbreviations_mechanical.csv',
'../journals/journal_abbreviations_medicus.csv',
'../journals/journal_abbreviations_meteorology.csv',
'../journals/journal_abbreviations_sociology.csv',
'../journals/journal_abbreviations_webofscience-dotless.csv',
'../journals/journal_abbreviations_webofscience-dots.csv'
'../journals/journal_abbreviations_sociology.csv'
]

@ -21,7 +21,7 @@
wxsFile.Close();
// Add registry values for JabRef Browser Extension
contents = contents.replace("</Product>", "<DirectoryRef Id=\"TARGETDIR\"><Component Id=\"RegistryJabRefBrowserEntries\" Guid=\"b6bc55ad-905c-4258-89b1-8b37abbe559c\" Win64=\"yes\"><RegistryKey Root=\"HKMU\" Key=\"SOFTWARE\\Mozilla\\NativeMessagingHosts\\org.jabref.jabref\" Action=\"createAndRemoveOnUninstall\" ForceCreateOnInstall=\"yes\"><RegistryValue Type=\"string\" Value=\"[INSTALLDIR]jabref-firefox.json\"/></RegistryKey><RegistryKey Root=\"HKMU\" Key=\"Software\\Google\\Chrome\\NativeMessagingHosts\\org.jabref.jabref\" Action=\"createAndRemoveOnUninstall\" ForceCreateOnInstall=\"yes\"><RegistryValue Type=\"string\" Value=\"[INSTALLDIR]jabref-chrome.json\"/></RegistryKey><RegistryKey Root=\"HKMU\" Key=\"Software\\Microsoft\\Edge\\NativeMessagingHosts\\org.jabref.jabref\" Action=\"createAndRemoveOnUninstall\" ForceCreateOnInstall=\"yes\"><RegistryValue Type=\"string\" Value=\"[INSTALLDIR]jabref-chrome.json\"/></RegistryKey><RegistryKey Root=\"HKMU\" Key=\"Software\\Google\\Chrome\\Extensions\\bifehkofibaamoeaopjglfkddgkijdlh\" Action=\"createAndRemoveOnUninstall\" ForceCreateOnInstall=\"yes\"><RegistryValue Type=\"string\" Name=\"update_url\" Value=\"https://clients2.google.com/service/update2/crx\" /></RegistryKey><RegistryKey Root=\"HKMU\" Key=\"Software\\Wow6432Node\\Google\\Chrome\\Extensions\\bifehkofibaamoeaopjglfkddgkijdlh\" Action=\"createAndRemoveOnUninstall\" ForceCreateOnInstall=\"yes\"><RegistryValue Type=\"string\" Name=\"update_url\" Value=\"https://clients2.google.com/service/update2/crx\" /></RegistryKey><RegistryKey Root=\"HKMU\" Key=\"Software\\Microsoft\\Edge\\Extensions\\pgkajmkfgbehiomipedjhoddkejohfna\" Action=\"createAndRemoveOnUninstall\" ForceCreateOnInstall=\"yes\"><RegistryValue Type=\"string\" Name=\"update_url\" Value=\"https://edge.microsoft.com/extensionwebstorebase/v1/crx\" /></RegistryKey><RegistryKey Root=\"HKMU\" Key=\"Software\\Wow6432Node\\Microsoft\\Edge\\Extensions\\pgkajmkfgbehiomipedjhoddkejohfna\" Action=\"createAndRemoveOnUninstall\" ForceCreateOnInstall=\"yes\"><RegistryValue Type=\"string\" Name=\"update_url\" Value=\"https://edge.microsoft.com/extensionwebstorebase/v1/crx\" /></RegistryKey></Component></DirectoryRef><Feature Id=\"BrowserExtension\" Level=\"1\"><ComponentRef Id=\"RegistryJabRefBrowserEntries\" /></Feature></Product>");
contents = contents.replace("</Product>", "<DirectoryRef Id=\"TARGETDIR\"><Component Id=\"RegistryJabRefBrowserEntries\" Guid=\"b6bc55ad-905c-4258-89b1-8b37abbe559c\" Win64=\"yes\"><RegistryKey Root=\"HKMU\" Key=\"SOFTWARE\\Mozilla\\NativeMessagingHosts\\org.jabref.jabref\" Action=\"createAndRemoveOnUninstall\" ForceCreateOnInstall=\"yes\"><RegistryValue Type=\"string\" Value=\"[INSTALLDIR]jabref-firefox.json\"/></RegistryKey><RegistryKey Root=\"HKMU\" Key=\"Software\\Google\\Chrome\\NativeMessagingHosts\\org.jabref.jabref\" Action=\"createAndRemoveOnUninstall\" ForceCreateOnInstall=\"yes\"><RegistryValue Type=\"string\" Value=\"[INSTALLDIR]jabref-chrome.json\"/></RegistryKey><RegistryKey Root=\"HKMU\" Key=\"Software\\Microsoft\\Edge\\NativeMessagingHosts\\org.jabref.jabref\" Action=\"createAndRemoveOnUninstall\" ForceCreateOnInstall=\"yes\"><RegistryValue Type=\"string\" Value=\"[INSTALLDIR]jabref-chrome.json\"/></RegistryKey></Component></DirectoryRef><Feature Id=\"BrowserExtension\" Level=\"1\"><ComponentRef Id=\"RegistryJabRefBrowserEntries\" /></Feature></Product>");
// Specify banner
contents = contents.replace("</Product>", "<WixVariable Id=\"WixUIBannerBmp\" Value=\"JabRefTopBanner.bmp\" /></Product>");

@ -293,4 +293,4 @@
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</code_scheme>

@ -33,13 +33,6 @@
<property name="fileExtensions" value="groovy,java" />
</module>
<!-- Prevent newline after catch opening parenthesis -->
<module name="RegexpMultiline">
<property name="message" value="No newline allowed after catch opening parenthesis" />
<property name="format" value="catch\s*\(\s*[\r]?\n" />
<property name="fileExtensions" value="groovy,java" />
</module>
<!-- Checks for size violations: https://checkstyle.sourceforge.io/config_sizes.html -->
<!-- LineLength not in place as PreviewerViewer and RelatedArticlesTab have line length with more than 500 charachters -->

@ -33,13 +33,6 @@
<property name="fileExtensions" value="groovy,java" />
</module>
<!-- Prevent newline after catch opening parenthesis -->
<module name="RegexpMultiline">
<property name="message" value="No newline allowed after catch opening parenthesis" />
<property name="format" value="catch\s*\(\s*[\r]?\n" />
<property name="fileExtensions" value="groovy,java" />
</module>
<!-- Checks for size violations: https://checkstyle.sourceforge.io/config_sizes.html -->
<!-- LineLength not in place as PreviewerViewer and RelatedArticlesTab have line length with more than 500 charachters -->

@ -1,8 +1,8 @@
FROM ruby:2.7
ENV LC_ALL=C.UTF-8
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US.UTF-8
ENV LC_ALL C.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
EXPOSE 4000

@ -1,11 +1,11 @@
source 'https://rubygems.org'
gem "jekyll", "~> 4.3.4" # installed by `gem jekyll`
gem "jekyll", "~> 4.3" # installed by `gem jekyll`
# Homepage: https://github.com/paulrobertlloyd/jekyll-figure#jekyll-figure
gem 'jekyll-figure'
gem "just-the-docs", "0.10.0"
gem "just-the-docs", "0.5.0"
gem "jekyll-remote-theme"

@ -10,8 +10,6 @@ exclude: [_config.yml, .dockerignore, .gitignore, CNAME, Dockerfile, Gemfile, Ge
# Browse alternative themes at https://stylishthemes.github.io/Syntax-Themes/pygments/
highlight_theme: jellybeans
favicon_ico: "/images/favicon.ico"
search_enabled: true
button: true
aux_links:

@ -1,19 +0,0 @@
---
parent: Code Howtos
---
# JabRef's handling of BibTeX
The main class to handle a single BibTeX entry is `org.jabref.model.entry.BibEntry`.
The content of a `.bib` file is handled in `org.jabref.model.database.BibDatabase`.
Things not written in the `.bib` file, but required for handling are stored in `org.jabref.model.database.BibDatabaseContext`.
For instance, this stores the mode of the library, which can be BibTeX or `biblatex`.
Standard BibTeX fields known to JabRef are modeled in `org.jabref.model.entry.field.StandardField`.
A user-defined field not known to JabRef's code is modelled in `org.jabref.model.entry.field.UnknownField`.
Typically, to get from a String to a `Field`, one needs to use `org.jabref.model.entry.field.FieldFactory#parseField(java.lang.String)`.
## Cross-references
BibTeX allows for referencing other entries by the field `crossref` (`org.jabref.model.entry.field.StandardField#CROSSREF`).
Note that BibTeX and `biblatex` handle this differently.
The method `org.jabref.model.entry.BibEntry#getResolvedFieldOrAlias(org.jabref.model.entry.field.Field, org.jabref.model.database.BibDatabase)` handles this difference.

@ -61,16 +61,7 @@ The import statement for all the classes using this class will be automatically
More information on the architecture can be found at [../getting-into-the-code/high-level-documentation.md](High-level documentation).
### `Check external href links in the documentation / lychee (push)` <span style="color:red">FAILED</span>
This test is triggered when any kind of documentation is touched (be it the JabRef docs, or JavaDoc in code). If you changed something in the documentation, and particularly added/changed any links (to external files or websites), check if the links are correct and working. If you didn't change/add any link, or added correct links, the test is most probably failing due to any of the existing links being broken, and thus can be ignored (in the context of your contribution).
### Failing <b>Fetcher</b> tests
Fetcher tests are run when any file in the `.../fetcher` directory has been touched. If you have changed any fetcher logic, check if the changes are correct. You can look for more details on how to locally run fetcher tests [here](https://devdocs.jabref.org/code-howtos/testing.html#fetchers-in-tests).
Otherwise, since these tests depend on remote services, their failure can also be caused by the network or an external server, and thus can be ignored in the context of your contribution. For more information, you can look [here](https://devdocs.jabref.org/code-howtos/fetchers.html#committing-and-pushing-changes-to-fetcher-files).
## Gradle outputs
## Gradle outpus
### `ANTLR Tool version 4.12.0 used for code generation does not match the current runtime version 4.13.1`
@ -81,72 +72,4 @@ Execute the Gradle task `clean` from the `build` group of the Gradle Tool Window
Execute gradle task `clean` from the `build` group of the Gradle Tool Window in IntelliJ.
### `No test candidates found`
You probably chose the wrong gradle task:
![Gradle tests](../images/gradle-tests.png)<br>
## Submodules
### The problem
Sometimes, when contributing to JabRef, you may see `abbrv.jabref.org` or `csl-styles` or `csl-locales` among the changed files in your pull request. This means that you have accidentally committed your local submodules into the branch.
![Changed submodules](../images/submodules.png)
### Context
JabRef needs external submodules (such as CSL style files) for some of its respective features. These are cloned once when you set up a local development environment, using `--recurse-submodules` (you may have noticed). These submodules, in the main branch, are automatically periodically updated but not fetched into local again when you pull, as they are set to be ignored in `.gitmodules` (this is to avoid merge conflicts). So when remote has updated submodules, and your local has the old ones, when you stage all files, these changes are noticed.
What's strange (mostly an IntelliJ bug): Regardless of CLI or GUI, These changes should ideally not be noticed on staging, as per the `.gitmodules` configuration. However, that is somehow overruled when using IntelliJ's CLI.
### Fix
For `csl-styles`:
```bash
git merge origin/main
git checkout main -- src/main/resources/csl-styles
... git commit ...
git push
```
And similarly for `csl-locales` or `abbrv.jabref.org`.
#### Alternative method (if the above doesn't work)
1. Edit `.gitmodules`: comment out `ignore = all` (for the respective submodules you are trying to reset)
```gitignore
# ignore = all
```
2. `cd` into the changed submodules directory (lets say `csl-styles` was changed):
```bash
cd src/main/resources/csl-styles
```
3. Find the latest submodule commit id from remote (github):
![Submodule commits](../images/submodule-commit.png)
Here, in the case of `csl-styles`, it is `4e0902d`.
4. Checkout the commit:
```bash
git checkout 4e0902d
```
5. Now, IntelliJ's commit tab will notice that the submodules have been modified. This means we are on the right track.
6. Use IntelliJ's git manager (commit tab) or `git gui` to commit submodule changes only. Repeat steps 2-5 for other submodules that are shown as modified in the PR. Then, push these changes.
7. Revert the changes in `.gitmodules` (that you made in step 1).
### Prevention
To avoid this, avoid staging using `git add .` from CLI. Preferably use a GUI-based git manager, such as the one built in IntelliJ or open git gui from the command line. Even if you accidentally stage them, don't commit all files, selectively commit the files you touched using the GUI based tool, and push.
<!-- markdownlint-disable-file MD033 -->

@ -76,23 +76,10 @@ In `build.gradle`, these variables are filled:
"springerNatureAPIKey" : System.getenv('SpringerNatureAPIKey')
```
The `BuildInfo` class reads from that file and the key needs to be put into the map of default API keys in `JabRefCliPreferences::getDefaultFetcherKeys`.
The `BuildInfo` class reads from that file.
```java
keys.put(SpringerFetcher.FETCHER_NAME, buildInfo.springerNatureAPIKey);
```
The fetcher api key can then be obtained by calling the preferences.
```java
importerPreferences.getApiKey(SpringerFetcher.FETCHER_NAME);
new BuildInfo().springerNatureAPIKey
```
When executing `./gradlew run`, gradle executes `processResources` and populates `build/build.properties` accordingly. However, when working directly in the IDE, Eclipse keeps reading `build.properties` from `src/main/resources`. In IntelliJ, the task `JabRef Main` is executing `./gradlew processResources` before running JabRef from the IDE to ensure the `build.properties` is properly populated.
## Committing and pushing changes to fetcher files
Fetcher tests are run when a PR contains changes touching any file in the `src/main/java/org/jabref/logic/importer/fetcher/` directory.
Since these tests rely on remote services, some of them may fail due to the network or the external server.
To learn more about doing fetcher tests locally, see Fetchers in tests in [Testing](https://devdocs.jabref.org/code-howtos/testing.html).

@ -3,8 +3,23 @@ parent: Code Howtos
---
# HTTP Server
JabRef has a built-in http server.
For example, the resource for a library is implemented at [`org.jabref.http.server.LibraryResource`](https://github.com/JabRef/jabref/blob/main/src/main/java/org/jabref/http/server/LibraryResource.java).
## Get SSL Working
(Based on <https://stackoverflow.com/a/57511038/873282>)
Howto for Windows - other operating systems work similar:
1. As admin `choco install mkcert`
2. As admin: `mkcert -install`
3. `cd %APPDATA%\..\local\org.jabref\jabref\ssl`
4. `mkcert -pkcs12 jabref.desktop jabref localhost 127.0.0.1 ::1`
5. Rename the file to `server.p12`
Note: If you do not do this, you get following error message:
```text
Could not find server key store C:\Users\USERNAME\AppData\Local\org.jabref\jabref\ssl\server.p12.
```
## Start http server
@ -54,24 +69,3 @@ DEBUG: Server started.
IntelliJ Ultimate offers a Markdown-based http-client. One has to open the file `src/test/java/org/jabref/testutils/interactive/http/rest-api.http`.
Then, there are play buttons appearing for interacting with the server.
## Get SSL Working
When interacting with the [Microsoft Word AddIn](https://github.com/JabRef/JabRef-Word-Addin), a SSL-based connection is required.
[The Word-AddIn is currentely under development](https://github.com/JabRef/JabRef-Word-Addin/pull/568).
(Based on <https://stackoverflow.com/a/57511038/873282>)
Howto for Windows - other operating systems work similar:
1. As admin `choco install mkcert`
2. As admin: `mkcert -install`
3. `cd %APPDATA%\..\local\org.jabref\jabref\ssl`
4. `mkcert -pkcs12 jabref.desktop jabref localhost 127.0.0.1 ::1`
5. Rename the file to `server.p12`
Note: If you do not do this, you get following error message:
```text
Could not find server key store C:\Users\USERNAME\AppData\Local\org.jabref\jabref\ssl\server.p12.
```

@ -47,7 +47,7 @@ JabRef stores files relative to one of [multiple possible directories](https://d
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.logic.FilePreferences)
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
@ -59,7 +59,7 @@ When adding a file to a library, the path should be stored relative to "the best
This is implemented in `FileUtil`:
```java
org.jabref.logic.util.io.FileUtil.relativize(java.nio.file.Path, org.jabref.model.database.BibDatabaseContext, org.jabref.logic.FilePreferences)
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

@ -45,9 +45,8 @@ To write a localized string in FXML file, prepend it with `%`, like in this code
## 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 entry(s).", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entry(s).");`
* 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
* For pluralization, use a combined form. E.g., `Localization.lang("checked %0 entry(s)")`.
## Checking for correctness
@ -67,7 +66,3 @@ The tests in `org.jabref.logic.l10n.LocalizationConsistencyTest` check whether t
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.
## Background information
The localization is tested via the class [LocalizationConsistencyTest](https://github.com/JabRef/jabref/blob/main/src/test/java/org/jabref/logic/l10n/LocalizationConsistencyTest.java).

@ -140,16 +140,6 @@ docker run -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=jabref -p 3800:3307 mys
Set the environment variable `DBMS` to `mysql`.
## Fetchers in tests
Fetcher tests can be run locally by executing the Gradle task `fetcherTest`. This can be done by running the following command in the command line:
```shell
./gradlew fetcherTest
```
Alternatively, if one is using IntelliJ, this can also be done by double-clicking the `fetcherTest` task under the `other` group in the Gradle Tool window (`JabRef > Tasks > other > fetcherTest`).
## Advanced testing and further reading
On top of basic unit testing, there are more ways to test a software:

@ -8,29 +8,19 @@ parent: Code Howtos
* For a usual form, place the label above the text field
* If the user uses the form often to edit fields, then it might make sense to switch to left-aligned labels
## Designing GUI Confirmation Dialogs
## Designing GUI Confirmation dialogs
1. Avoid asking questions
2. Be as concise as possible
3. Identify the item at risk
4. Name your buttons for the actions
More information:
* [StackOverflow: What are some alternatives to the phrase "Are you sure you want to XYZ" in confirmation dialogs?](https://ux.stackexchange.com/q/756/93436).
* JabRef issue discussing Yes/No/Cancel: [koppor#149](https://github.com/koppor/jabref/issues/149).
### Name your buttons for the actions
`req~ui.dialogs.confirmation.naming~1`
Needs: impl
More information: [http://ux.stackexchange.com/a/768](http://ux.stackexchange.com/a/768)
## Form validation
* Only validate input after leaving the field (or after the user stopped typing for some time)
* The user shouldn't be able to submit the form if there are errors
* However, disabling the submit button in case there are errors is also not optimal. Instead, clicking the submit button should highlight the errors.
* Empty required files should not be marked as invalid until the user a) tried to submit the form or b) focused the field, deleted its contents and then left the field (see [Example](https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_required)).
* Ideally, the error message should be shown below the text field and not as a tooltip (so that users quickly understand what's the problem). For example as [in Boostrap](https://mdbootstrap.com/docs/jquery/forms/validation/?#custom-styles).
<!-- markdownlint-disable-file MD022 -->
* Empty required files shouldn't be marked as invalid until the user a) tries to submit the form or b) focused the field, deleted it contents and then left the field (see [Example](https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_required)
* Ideally, the error message should be shown below the text field and not as a tooltip (so that users quickly understand what's the problem). For example as here [in Boostrap](https://mdbootstrap.com/docs/jquery/forms/validation/?#custom-styles)

@ -3,4 +3,112 @@ nav_order: 2
---
# Contributing
Please head to our [contributing guide in the main repository](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md#contributing).
* After reading through this guide, check out some good first issues to contribute to by going to our [list of good first issues](https://github.com/orgs/JabRef/projects/5/views/1).
We offer small issues perfect for aspiring developers. These tasks provide an opportunity to learn how to set up your local workspace, create your first pull request on GitHub, and contribute towards solving minor problems or making small enhancements in JabRef. It is essential to note that the issues vary in difficulty. Some are simpler, while others are more complex. Our primary aim is to guide you through the code, ensuring that the understanding scope remains manageable. Sometimes, grasping the code might demand more effort than actually writing lines of code.
* In case you are aiming to contribute other improvements, please head over to our [general JabRef contribution page](https://docs.jabref.org/faqcontributing).
* In case you are an instructor and want to use **JabRef as a software engineering example**, please head to [https://devdocs.jabref.org/teaching](https://devdocs.jabref.org/teaching).
## Contribute code
### Understanding the basics of code contributions
We welcome contributions to JabRef and encourage you to follow the GitHub workflow specified below. If you are not familiar with this type of workflow, take a look at GitHub's excellent overview on the [GitHub flow](https://docs.github.com/en/get-started/using-github/github-flow) and the explanation of [Feature Branch Workflow](https://atlassian.com/git/tutorials/comparing-workflows#feature-branch-workflow) for the idea behind this kind of development.
1. Get the JabRef code on your local machine. Detailed instructions about this step can be found in our [guidelines for setting up a local workspace](getting-into-the-code/guidelines-for-setting-up-a-local-workspace/).
1. Fork the JabRef into your GitHub account.
2. Clone your forked repository on your local machine.
2. **Create a new branch** (such as `fix-for-issue-121`). Be sure to create a **separate branch** for each improvement you implement.
3. Work on the **new branch — not the `main` branch.** Refer to our [code how-tos](https://devdocs.jabref.org/code-howtos) if you have questions about your implementation.
4. Create a pull request. For an overview of pull requests, take a look at GitHub's [pull request help documentation](https://help.github.com/articles/about-pull-requests/).
5. In case your pull request is not yet complete or not yet ready for review, create a [draft pull request](https://github.blog/2019-02-14-introducing-draft-pull-requests/) instead.
In case you have any questions, please comment on the issue - or show up in our [gitter chat](https://gitter.im/JabRef/jabref).
Please also help others in case of general questions there.
### Formal requirements for a pull request
The main goal of the formal requirements is to provide credit to you and to be able to understand the patch.
#### Add your change to `CHANGELOG.md`
You should edit the [`CHANGELOG.md`](https://github.com/JabRef/jabref/blob/main/CHANGELOG.md#changelog) file located in the root directory of the JabRef source. Add a line with your changes in the appropriate section.
If you did internal refactorings or improvements not visible to the user (e.g., UI, .bib file), then you don't need to put an entry there.
#### **Format of keyboard shortcuts**
Example: `<kbd>Ctrl</kbd> + <kbd>Enter</kbd>`
In case you add keys to the changelog, please follow these rules:
* `<kbd>` tag for each key
* First letter of key capitalized
* Combined keys separated by `+`
* Spaces before and after separator `+`
#### Author credits
Please, **do not add yourself at JavaDoc's `@authors`**. The contribution information is tracked via the version control system and shown at [https://github.com/JabRef/jabref/graphs/contributors](https://github.com/JabRef/jabref/graphs/contributors). We also link to the contributors page in our about dialog.
Your contribution is considered being made under [MIT license](https://tldrlegal.com/license/mit-license).
#### Write a good commit message
See [good commit message](https://github.com/joelparkerhenderson/git-commit-message) or [commit guidelines section of Pro Git](http://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines). For the curious: [Why good commit messages matter!](https://cbea.ms/git-commit/). The first line of your commit message is automatically taken as the title for the pull-request. All other lines make up the body of the pull request. Add the words `fixes #xxx` to your PR to auto-close the corresponding issue.
#### Test your code
We know that writing test cases takes a lot of time. Nevertheless, we rely on our test cases to ensure that a bug fix or a feature implementation doesn't break anything. In case you do not have time to add a test case, we nevertheless ask you to at least run `gradlew check` to ensure that your change doesn't break anything else.
#### When adding a new `Localization.lang` entry
Add new `Localization.lang("KEY")` to a Java file. The tests will fail. In the test output a snippet is generated, which must be added to the English translation file.
Example:
```text
java.lang.AssertionError: DETECTED LANGUAGE KEYS WHICH ARE NOT IN THE ENGLISH LANGUAGE FILE
PASTE THESE INTO THE ENGLISH LANGUAGE FILE
[
Opens\ JabRef's\ Twitter\ page=Opens JabRef's Twitter page
]
Expected :[]
Actual :[Opens\ JabRef's\ Twitter\ page (src\main\java\org\jabref\gui\JabRefFrame.java LANG)]
```
Add the above snippet to the English translation file located at `src/main/resources/l10n/JabRef_en.properties`. [Crowdin](https://crowdin.com/project/jabref) will automatically pick up the new string and add it to the other translations.
You can also directly run the specific test in your IDE. The test "LocalizationConsistencyTest" is placed under `src/test/java/org.jabref.logic.l10n/LocalizationConsistencyTest.java`. Find more information in the [JabRef developer docs](code-howtos/localization.md).
#### When adding a library
Please try to use a version available at JCenter and add it to `build.gradle`. In any case, describe the library at [`external-libraries.md`](https://github.com/JabRef/jabref/blob/main/external-libraries.md#external-libraries). We need that information for our package maintainers (e.g., those of the [debian package](https://tracker.debian.org/pkg/jabref)). Also add a txt file stating the license in `libraries/`. It is used at `gradlew processResources` to generate the About.html files. You can see the result in `build\resources\main\help\en\About.html` or when clicking Help -> About.
#### When making an architectural decision
In case you add a library or do major code rewrites, we ask you to document your decision. Recommended reading: [https://adr.github.io/](https://adr.github.io).
We simply ask to create a new markdown file in `docs/adr` following the template presented at [https://adr.github.io/madr/](https://adr.github.io/madr/).
In case you want to directly add a comment to a class, simply use the following template (based on [sustainable architectural decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions)):
```text
In the context of <use case/user story u>,
facing <concern c>
we decided for <option o>
and neglected <other options>,
to achieve <system qualities/desired consequences>,
accepting <downside / undesired consequences>,
because <additional rationale>.
```
### Create a pull request
Create a pull request on GitHub following GitHub's guide "[Creating a pull request from a fork](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork)". For text inspirations, consider [How to write the perfect pull request](https://github.com/blog/1943-how-to-write-the-perfect-pull-request).
If you want to indicate that a pull request is not yet complete **before** creating the pull request, you may consider creating a [draft pull request](https://github.blog/2019-02-14-introducing-draft-pull-requests/). Alternatively, once the PR has been created, you can add the prefix `[WIP]` (which stands for "Work in Progress") to indicate that the pull request is not yet complete, but you want to discuss something or inform about the current state of affairs.
## How to improve the developer's documentation
For improving developer's documentation, go on at the [docs/ subdirectory of JabRef's code](https://github.com/JabRef/jabref/tree/main/docs) and edit the file.
GitHub offers a good guide at [Editing files in another user's repository](https://help.github.com/en/github/managing-files-in-a-repository/editing-files-in-another-users-repository).

@ -2,16 +2,15 @@
parent: Decision Records
nav_order: 0
---
# Use Markdown Architectural Decision Records
# Use Markdown Any Decision Records
## Context and Problem Statement
We want to record architectural decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields.
We want to record any decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields.
Which format and structure should these records follow?
## Considered Options
* [MADR](https://adr.github.io/madr/) 4.0.0 The Markdown Architectural Decision Records
* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) The first incarnation of the term "ADR"
* [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) The Y-Statements
* Other templates listed at <https://github.com/joelparkerhenderson/architecture_decision_record>
@ -19,11 +18,11 @@ Which format and structure should these records follow?
## Decision Outcome
Chosen option: "MADR 4.0.0", because
Chosen option: "MADR", because
* Implicit assumptions should be made explicit.
Design documentation is important to enable people understanding the decisions later on.
See also ["A rational design process: How and why to fake it"](https://doi.org/10.1109/TSE.1986.6312940).
See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940).
* MADR allows for structured capturing of any decision.
* The MADR format is lean and fits our development style.
* The MADR structure is comprehensible and facilitates usage & maintenance.

@ -22,7 +22,7 @@ JabRef has translation files `JabRef_it.properties`, ... There are translated an
## Decision Outcome
Chosen option: "Only translated strings in language file", because comes out best (see below).
Chosen option: "Only translated strings in language file", because comes out best \(see below.
## Pros and Cons of the Options

@ -6,7 +6,7 @@ nav_order: 7
## Context and Problem Statement
Changes of a release have to be communicated. How and which style to use?
Changes of a release have to be communicated. How and which stile to use?
## Considered Options

@ -22,7 +22,7 @@ For high-quality documentation, external links should be working.
## Decision Outcome
Chosen option: "Check external links once a month", because it provides a basic quality baseline.
Chosen option: "\[option 1\]", because \[justification. e.g., only option, which meets k.o. criterion decision driver \| which resolves force force \| … \| comes out best \(see below\)\].
### Positive Consequences
@ -30,8 +30,7 @@ Chosen option: "Check external links once a month", because it provides a basic
### Negative Consequences
* Some external sites need to [be disabled](https://github.com/JabRef/jabref/pull/6542/files). For instance, GitHub.com always returns "forbidden".
* Contributors find it strange if external links are broken (example: [user-documentation#526](https://github.com/JabRef/user-documentation/pull/526#issuecomment-2416462977))
* Some external sites need to [be disabled](https://github.com/JabRef/jabref/pull/6542/files). For instance, GitHub.com always returns "forbidden". A [filter for status is future work of the used tool](https://github.com/tcort/markdown-link-check/issues/94#issuecomment-634947466).
## Pros and Cons of the Options

@ -17,7 +17,7 @@ How to implement special fields in BibTeX databases?
## Decision Outcome
Chosen option: "Special fields as separate fields", because comes out best (see below).
Chosen option: "Special fields as separate fields", because comes out best (see below)
## Pros and Cons of the Options

@ -20,7 +20,7 @@ What parser should be used to parse YAML files?
## Decision Outcome
Chosen option: "Jackson", because as it is a dedicated library for parsing YAML. `yamlbeans` also seem to be viable. They all offer similar functionality.
Chosen option: Jackson, because as it is a dedicated library for parsing YAML. `yamlbeans` also seem to be viable. They all offer similar functionality.
## Pros and Cons of the Options

@ -25,7 +25,7 @@ This decision record concerns the UI component that is used for rendering the co
## Decision Outcome
Chosen option: "Use `TextArea`".
Chosen option: "Use TextArea".
All other options require more time to implement.
Some of the options do not support text selection and copying,
which for now we value more than Markdown rendering.

@ -1,114 +0,0 @@
---
nav_order: 39
parent: Decision Records
---
# Use Apache Velocity as template engine
## Context and Problem Statement
We need to choose a template engine for [custom export filters](https://docs.jabref.org/collaborative-work/export/customexports) and [AI features](https://github.com/JabRef/jabref/pull/11884).
A discussion of template engines was also in [one of the JabRef repos](https://github.com/koppor/jabref/issues/392).
A discussion was raised on StackOverflow ["Velocity vs. FreeMarker vs. Thymeleaf"](https://stackoverflow.com/q/1459426/10037342).
## Decision Drivers
* It should be fast.
* It should be possible to provide templates out of `String`s (required by the AI feature).
* It should have short and understandable syntax. Especially, it should work well with unset fields and empty `Optional`s.
## Considered Options
* Apache Velocity
* Apache FreeMarker
* Thymeleaf
## Decision Outcome
Chosen option: "Apache Velocity", because "Velocity's goal is to keep templates as simple as possible" ([source](https://stackoverflow.com/a/1984458/873282)). It is sufficient for our use case.
Furthermore, Apache Velocity is lightweight, and it allows to generate text output. This is a good fit for the AI feature.
## Pros and Cons of the Options
### Apache Velocity
- Main page: <https://velocity.apache.org/>.
- User guide: <https://velocity.apache.org/engine/devel/user-guide.html>.
- Developer guide: <https://velocity.apache.org/engine/devel/developer-guide.html>.
Example:
```text
You are an AI assistant that analyses research papers. You answer questions about papers.
Here are the papers you are analyzing:
#foreach( $entry in $entries )
${CanonicalBibEntry.getCanonicalRepresentation($entry)}
#end
```
* Good, because supports plain text templating.
* Good, because it is possible to use `String` as a template.
* Good, because it has simple syntax, and it is designed for simple template workflows.
* Good, because it has a stable syntax ([source](https://stackoverflow.com/a/1984458/10037342)).
* Bad, because it is in maintenance mode.
* Bad, because [removed from Spring 5.0.1](https://www.baeldung.com/spring-template-engines#other-template-engines)
### Apache FreeMarker
- Main page: <https://freemarker.apache.org/index.html>.
- User guide: <https://freemarker.apache.org/docs/dgui.html>.
- Developer guide: <https://freemarker.apache.org/docs/pgui_quickstart.html>.
Example:
```text
You are an AI assistant that analyzes research papers. You answer questions about papers.
Here are the papers you are analyzing:
<#list entries as entry>
${CanonicalBibEntry.getCanonicalRepresentation(entry)}
</#list>
```
* Good, because supports plain text templating.
* Good, because it is possible to use `String` as a template.
* Good, because in active development.
* Good, because it is powerful and flexible.
* Good, because it has extensive documentation ([source](https://stackoverflow.com/a/1984458/10037342)).
* Neutral, because it has received some API and syntax changes recently ([source](https://stackoverflow.com/a/1984458/10037342)).
* Neutral, because FreeMarker is used for complex template workflow, which we do not need in JabRef.
### Thymeleaf
- Main page: <https://www.thymeleaf.org/>.
- Documentation: <https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html>.
Example:
```text
You are an AI assistant that analyzes research papers. You answer questions about papers.
Here are the papers you are analyzing:
[# th:each="entry : ${entries}"]
[(${CanonicalBibEntry.getCanonicalRepresentation(entry)})]
[/]
```
* Good, because supports plain text templating.
* Good, because it is possible to use `String` as a template.
* Good, because it has [several template modes](https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#what-kind-of-templates-can-thymeleaf-process), that helps to make HTML, XML, and other templates.
* Good, because it is powerful and flexible.
* Neutral, because the API is a bit more complex than the other options.
* Bad, because the syntax is more complex than the other options. Especially for text output.
## More Information
As stated in [the template discussion issue](https://github.com/koppor/jabref/issues/392), we should choose a template engine, and then slowly migrate previous code and templates to the chosen engine.
Other template engines are discussed at <https://www.baeldung.com/spring-template-engines>, especially [`#other-template-engines`](https://www.baeldung.com/spring-template-engines#other-template-engines).
We did not find any other engine there worth switching to.
<!-- markdownlint-disable-file MD004 -->

@ -1,61 +0,0 @@
---
parent: Decision Records
nav_order: 40
---
# Display front cover for book citations in the Preview tab
## Context and Problem Statement
* Users have requested that the front covers of book citations are displayed in JabRef.
* This is discussed on the [JabRef forum](https://discourse.jabref.org/t/display-cover-images-for-books/3647) and raised as a [feature request](https://github.com/JabRef/jabref/issues/10120).
* We need to decide where the book cover should be placed.
## Decision Drivers
* It should not be obtrusive or distracting since the main use of JabRef is for articles not books.
* It should not obstruct the view of existing GUI components, specifically the MainTable or the information in the EntryEditor's tabs.
## Considered Options
Place the book cover in:
1. The existing SidePane
2. A new SidePane
3. The Preview panel of the EntryEditor
4. A SplitPane next to the MainTable
## Decision Outcome
Chosen option: "The PreviewPanel of the EntryEditor".
## Pros and Cons of the Options
### Existing SidePane
![Image: Placement in SidePane](0040-placement-in-sidepane.png)
* Good, because it would be unobtrusive
* Bad, because it would crowd other panels in the SidePane
* Bad, because changing the size of the SidePane would affect both the MainTable and the EntryEditor.
### New right-sided SidePane
![Image: Placement in the new right-sided SidePane](0040-placement-in-new-sidepane.png)
* Good, if integrated together with entry preview because it would make it easier to view a citation's preview.
* Bad, because an extra SidePane would make the interface overly complex.
### The PreviewPanel of the EntryEditor
![Image: Placement in the Preview Panel](0040-placement-in-preview-panel.png)
* Good, because it would not be obtrusive or distracting.
* Bad, if the Entry Editor is closed, users will have to open the Entry Editor and navigate to the "Preview" or "Required fields" tab to see the cover.
### SplitPane next to the MainTable
![Image: Placement next to the Main Table](0040-placement-in-maintable.png)
* Good, because changing the size of this SplitPane would [only affect the MainTable](https://github.com/user-attachments/assets/4e458099-ca5c-41bc-a33b-ce4240d7df82).
* Bad, because it would obstruct some columns in the MainTable.

Binary file not shown.

Before

(image error) Size: 61 KiB

Binary file not shown.

Before

(image error) Size: 75 KiB

Binary file not shown.

Before

(image error) Size: 78 KiB

Binary file not shown.

Before

(image error) Size: 62 KiB

@ -1,95 +0,0 @@
---
nav_order: 41
parent: Decision Records
---
<!-- we need to disable MD025, because we use the different heading "ADR Template" in the homepage (see above) than it is foreseen in the template -->
<!-- markdownlint-disable-next-line MD025 -->
# Use one language string for pluralization localization
## Context and Problem Statement
For user-facing messages, sometimes, it needs to be counted: E.g., 1 entry updated, 2 entries updated, etc.
In some languages, there is not only "one" and "more than one", but other forms:
* zero → “لم نزرع أي شجرة حتى الآن”
* one → “لقد زرعنا شجرة ١ حتى الآن”
* two → “لقد زرعنا شجرتين ٢ حتى الآن”
* few → “لقد زرعنا ٣ شجرات حتى الآن”
* many → “لقد زرعنا ١١ شجرة حتى الآن”
* other → “لقد زرعنا ١٠٠ شجرة حتى الآن”
(Example is from [Pluralization: A Guide to Localizing Plurals](https://phrase.com/blog/posts/pluralization/))
How to localize pluralization?
## Decision Drivers
* Good English language
* Good localization to other languages
## Considered Options
* Use one language string for pluralization (no explicit pluralization)
* Use singular and plural
* Handling of multiple forms
## Decision Outcome
Chosen option: "Use one form only (no explicit pluralization)", because it is the most easiest to handle in the code.
## Pros and Cons of the Options
### Use one language string for pluralization (no explicit pluralization)
Example:
- `Imported 0 entry(s)`
- `Imported 1 entry(s)`
- `Imported 12 entry(s)`
There are sub alternatives here:
* `Imported %0 entry(ies)`.
* `Number of entries imported: %0` (always use "other" plural form)
These arguments are for the general case of using a single text for all kinds of numbers:
* Good, because easy to handle in the code
* Bad, because reads strange in English UI
### Use singular and plural
Example:
- `Imported 0 entries`
- `Imported 1 entry`
- `Imported 12 entries`
* Good, because reads well in English
* Bad, because all localizations need to take an `if` check for the count
* Bad, because Arabic not localized properly
### Handling of multiple forms
Example:
- `Imported 0 entries`
- `Imported 1 entry`
- `Imported 12 entries`
Code: `Localization.lang("Imported %0 entries", "Imported %0 entry.", "Imported %0 entries.", "Imported %0 entries.", "Imported %0 entries.", "Imported %0 entries.", count)`
* Good, because reads well in English
* Bad, because sophisticated localization handling is required
* Bad, because no Java library for handling pluralization is known
* Bad, because Arabic not localized properly
## More Information
- [Language Plural Rules](https://www.unicode.org/cldr/charts/43/supplemental/language_plural_rules.html)
- [Unicode CLDR Project's Plural Rules](https://cldr.unicode.org/index/cldr-spec/plural-rules)
- [Implementation in Mozilla Firefox](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules)
- [SX discussion on plural forms](https://english.stackexchange.com/a/90283/66058)
<!-- markdownlint-disable-file MD004 -->

@ -9,16 +9,16 @@ nav_order: 100
# Keep this
parent: Decision Records
# These are optional metadata elements. Feel free to remove any of them.
# status: "{proposed | rejected | accepted | deprecated | … | superseded by ADR-0123"
# These are optional elements. Feel free to remove any of them.
# status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)}
# date: {YYYY-MM-DD when the decision was last updated}
# decision-makers: {list everyone involved in the decision}
# deciders: {list everyone involved in the decision}
# consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication}
# informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication}
---
<!-- we need to disable MD025, because we use the different heading "ADR Template" in the homepage (see above) than it is foreseen in the template -->
<!-- markdownlint-disable-next-line MD025 -->
# {short title, representative of solved problem and found solution}
# {short title of solved problem and solution}
## Context and Problem Statement
@ -41,19 +41,25 @@ parent: Decision Records
## Decision Outcome
Chosen option: "{title of option 1}", because {justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}.
Chosen option: "{title of option 1}", because
{justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}.
<!-- This is an optional element. Feel free to remove. -->
### Consequences
### Positive Consequences
* Good, because {positive consequence, e.g., improvement of one or more desired qualities, …}
* Bad, because {negative consequence, e.g., compromising one or more desired qualities, …}
* … <!-- numbers of consequences can vary -->
* {e.g., improvement of one or more desired qualities, …}
* …
<!-- This is an optional element. Feel free to remove. -->
### Confirmation
### Negative Consequences
{Describe how the implementation of/compliance with the ADR can/will be confirmed. Is the chosen design and its implementation in line with the decision? E.g., a design/code review or a test with a library such as ArchUnit can help validate this. Note that although we classify this element as optional, it is included in many ADRs.}
* {e.g., compromising one or more desired qualities, …}
* …
<!-- This is an optional element. Feel free to remove. -->
## Validation
{describe how the implementation of/compliance with the ADR is validated. E.g., by a review or an ArchUnit test}
<!-- This is an optional element. Feel free to remove. -->
## Pros and Cons of the Options
@ -83,4 +89,8 @@ Chosen option: "{title of option 1}", because {justification. e.g., only option,
<!-- This is an optional element. Feel free to remove. -->
## More Information
{You might want to provide additional evidence/confidence for the decision outcome here and/or document the team agreement on the decision and/or define when/how this decision the decision should be realized and if/when it should be re-visited. Links to other decisions and resources might appear here as well.}
{You might want to provide additional evidence/confidence for the decision outcome here and/or
document the team agreement on the decision and/or
define when this decision when and how the decision should be realized and if/when it should be re-visited and/or
how the decision is validated.
Links to other decisions and resources might here appear as well.}

@ -4,7 +4,7 @@ has_children: true
---
# Decision Records
Below, all "architectural decision records" for JabRef are listed.
Below, all "any decision records" for JabRef are listed.
This list uses the TOC functionality of the [Just the Docs Jekyll template](https://just-the-docs.github.io/just-the-docs/).
For new ADRs, please use [adr-template.md](adr-template.md) as basis.

@ -62,19 +62,4 @@ This will make these directories "Unregistered roots:", which is fine.
![Directory Mappings having three repositories unregsitered](intellij-directory-mappings-unregistered-roots.png)
{% endfigure %}
## Ensure that committing with other tools work
Open a "git bash".
On Windows, navigate to `C:\git-repositories\JabRef`.
Open the context menu of the file explorer (using the right mouse button), choose "Open Git Bash here".
Execute following command:
```shell
git update-index --assume-unchanged buildres/abbrv.jabref.org src/main/resources/csl-styles src/main/resources/csl-locales
```
{: .tip }
If you do not see the context menu, re-install git following the steps given at [StackOverflow](https://stackoverflow.com/a/50667280/873282).
<!-- markdownlint-disable-file MD033 -->

@ -6,24 +6,24 @@ nav_order: 12
# Step 2: Set up the build system: JDK and Gradle
## Ensure that JDK 23 is available to IntelliJ
## Ensure that JDK 21 is available to IntelliJ
Ensure you have a Java 23 SDK configured by navigating to **File > Project Structure... > Platform Settings > SDKs**.<br>
Ensure you have a Java 21 SDK configured by navigating to **File > Project Structure... > Platform Settings > SDKs**.<br>
**Note:** In some MacBooks, `Project Structure` can be found at the "IntelliJ" button of the app menu instead of at "File".
{% figure caption:"JDKs 11, 14, and 15 shown in available SDKs. JDK 23 is missing." %}
{% figure caption:"JDKs 11, 14, and 15 shown in available SDKs. JDK 21 is missing." %}
![Plattform Settings - SDKs](intellij-choose-jdk-adoptopenjdk-on-windows-project-settings.png)
{% endfigure %}
If there is another JDK than JDK 23 selected, click on the plus button and choose "Download JDK..."
If there is another JDK than JDK 21 selected, click on the plus button and choose "Download JDK..."
{% figure caption:"Download JDK..." %}
![Plattform Settings - SDKs - plus button - Download JDK...](guidelines-select-download-jdk.png)
{% endfigure %}
Select JDK version 23 and then Eclipse Temurin.
Select JDK version 21 and then Eclipse Temurin.
{% figure caption:"Example for JDK 23 - Choose Eclipse Temurin" %}
{% figure caption:"Example for JDK 21 - Choose Eclipse Temurin" %}
![Download Eclipse Temurin](guidelines-intellij-select-jdk-eclipse-temurin.png)
{% endfigure %}
@ -33,10 +33,10 @@ After clicking "Download", IntelliJ installs Eclipse Temurin:
![IntelliJ installs Eclipse Temurin](guidelines-intellij-installs-temurin.png)
{% endfigure %}
Navigate to **Project Settings > Project** and ensure that the projects' SDK is Java 23.
Navigate to **Project Settings > Project** and ensure that the projects' SDK is Java 21.
{% figure caption:"Project SDK is pinned to the downloaded SDK (showing JDK 23 as example)" %}
![Project SDK is JDK 23](guidelines-intellij-project-settings-jdk.png)
{% figure caption:"Project SDK is pinned to the downloaded SDK (showing JDK 21 as example)" %}
![Project SDK is JDK 21](guidelines-intellij-project-settings-jdk.png)
{% endfigure %}
Click "OK" to store the changes.
@ -44,13 +44,13 @@ Click "OK" to store the changes.
## Ensure correct JDK setting for Gradle
Navigate to **File > Settings... > Build, Execution, Deployment > Build Tools > Gradle** and select the "Project SDK" as the Gradle JVM at the bottom.
If that does not exist, just select JDK 23.
If that does not exist, just select JDK 21.
{% figure caption:"Gradle JVM is project SDK (showing "Projekt SDK temurin-23" as example)" %}
{% figure caption:"Gradle JVM is project SDK (showing "Projekt SDK temurin-21" as example)" %}
![Gradle JVM is project SDK](guidelines-intellij-settings-gradle-gradlejvm-is-projectjvm.png)
{% endfigure %}
## Enable compilation by IntelliJ
## Enable compiliation by IntelliJ
To prepare IntelliJ's build system additional steps are required:
@ -128,7 +128,7 @@ After that a new entry called "jabref \[run]" appears in the run configurations.
Now you can also select "jabref \[run]" and either run or debug the application from within IntelliJ.
{: .note }
You can run any other development task similarly.
You can run any other development task in a similar way.
## Using IntelliJ's internal build system for tests

@ -83,15 +83,3 @@ Another indication is following output
```text
java.lang.UnsupportedClassVersionError: org/javamodularity/moduleplugin/ModuleSystemPlugin has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
```
## Attempts to open preferences panel freezes application
This is likely caused by improper integration of your OS or Desktop Environment with your password prompting program or password manager. Ensure that these are working properly, then restart your machine and attempt to run the program.
In an ideal scenario, a password prompt should appear when the program starts, provided the keyring your OS uses has not already been unlocked. However, the implementation details vary depending on the operating system, which makes troubleshooting more complex.
For Windows and macOS users, specific configurations may differ based on the password management tools and settings used, so ensure your OS's password management system is properly set up and functioning.
For Linux users, ensure that your [xdg-desktop-portal](https://wiki.archlinux.org/title/XDG_Desktop_Portal) settings refer to active and valid portal implementations installed on your system. However, there might be other factors involved, so additional research or guidance specific to your distribution may be necessary.
For reference, see the discussion at issue [#11766](https://github.com/JabRef/jabref/issues/11766).

Binary file not shown.

Before

Width: 32px  |  Height: 32px  |  Size: 4.2 KiB

Binary file not shown.

Before

(image error) Size: 36 KiB

Binary file not shown.

Before

(image error) Size: 9.6 KiB

Binary file not shown.

Before

(image error) Size: 453 KiB

@ -1,16 +0,0 @@
---
parent: Requirements
---
# AI
## User Interface
### Chatting with AI
`req~ai.chat.new-message-based-on-previous~1`
To enable simple editing and resending of previous messages, <kbd>Cursor Up</kbd> should show last message.
This should only happen if the current text field is empty.
Needs: impl
<!-- markdownlint-disable-file MD022 -->

@ -1,49 +0,0 @@
---
nav_order: 7
has_children: true
---
# Requirements
This part of the documentation collects requirements using [OpenFastTrace](https://github.com/itsallcode/openfasttrace).
## Specifying requirements
One writes directly below a Markdown heading a requirement identifier.
Example:
```markdown
### Example
`req~ai.example~1`
```
It is important that there is no empty line directly after the heading.
{: note}
One needs to add `<!-- markdownlint-disable-file MD022 -->` to the end of the file, because the ID of the requirement needs to follow the heading directly.
## Linking implementations
Then, one writes down at the requirement.
Directly at the end, one writes that it requires an implementation:
```markdown
Needs: impl
```
One can also state that there should be detailed design document (`dsn`).
However, typically in JabRef, we go from the requirement directly to the implementation.
Then, at the implementation, a comment is added this implementation is covered:
```java
// [impl->req~ai.example~1]
```
When executing the gradle task `traceRequirements`, `build/tracing.txt` is generated.
In case of a tracing error, one can inspect this file to see which requirements were not covered.
## More Information
- [User manual of OpenFastTrace](https://github.com/itsallcode/openfasttrace/blob/main/doc/user_guide.md)
- We cannot copy and paste real examples here, because of [openfasttrace#280](https://github.com/itsallcode/openfasttrace/issues/280).

@ -42,13 +42,6 @@ Note: It is important to include v1.5.54 or later as v1.5.54 is the first ver
(Sorted alphabetically by Id)
```yaml
Id: ai.djl.*:*
Project: Deep Java Library
URL: https://djl.ai/
License: Apache-2.0
```
```yaml
Id: at.favre.lib
Project: HMAC-based Key Derivation Function (HKDF) RFC 5869
@ -91,13 +84,6 @@ URL: https://github.com/FasterXML/jackson
License: Apache-2.0
```
```yaml
Id: com.knuddels:jtokkit
Project: JTokkit - Java Tokenizer Kit
URL: https://github.com/knuddelsgmbh/jtokkit
License: MIT
```
```yaml
Id: com.github.hypfvieh.dbus-java
Project: dbus-java
@ -225,6 +211,13 @@ URL: https://github.com/eclipse-ee4j/jaxb-istack-commons
License: BSD-3-Clause (with copyright as described in Eclipse Distribution License - v 1.0 - see https://wiki.spdx.org/view/Legal_Team/License_List/Licenses_Under_Consideration for details)
```
```yaml
Id: com.tobiasdiez:easybind
Project: EasyBind
URL: https://github.com/tobiasdiez/EasyBind
License: BSD-2-Clause
```
```yaml
Id: com.vladsch.flexmark:flexmark-all
Project: flexmark-java
@ -350,10 +343,10 @@ License: MIT
```
```yaml
Id: io.github.adr:e-adr
Project: EmbeddedArchitecturalDecisionRecords
URL: https://github.com/adr/e-adr/
License: EPL-2.0
Id:io.github.adr:e-adr
Project:EmbeddedArchitecturalDecisionRecords
URL:https://github.com/adr/e-adr/
License:EPL-2.0
```
```yaml
@ -363,13 +356,6 @@ URL: https://github.com/java-diff-utils/java-diff-utils
License: Apache-2.0
```
```yaml
Id: io.zonky.test:embedded-postgres
Project: embedded-postgres
URL: https://github.com/zonkyio/embedded-postgres
License: Apache-2.0
```
```yaml
Id: jakarta.annotation:jakarata.annotation-api
Project: Jakarta Annotations
@ -555,13 +541,6 @@ Path: lib/icu4j.jar
SourcePath: lib/ic4j-src.jar
```
```yaml
Id: org.jabref:easybind
Project: EasyBind
URL: https://github.com/JabRef/EasyBind
License: BSD-2-Clause
```
```yaml
Id: org.jooq:jool
Project: JOOλ
@ -720,13 +699,8 @@ License: BSD-3-Clause
3. (on WSL) `sed 's/[^a-z]*//' < build/reports/project/dependencies.txt | sed "s/\(.*\) .*/\1/" | grep -v "\->" | sort | uniq > build/dependencies-for-external-libraries.txt`
```text
ai.djl.huggingface:tokenizers:0.30.0
ai.djl.pytorch:pytorch-engine:0.30.0
ai.djl.pytorch:pytorch-model-zoo:0.30.0
ai.djl:api:0.30.0
ai.djl:bom:0.30.0
at.favre.lib:hkdf:1.1.0
com.dlsc.gemsfx:gemsfx:2.48.0
com.dlsc.gemsfx:gemsfx:2.32.0
com.dlsc.pickerfx:pickerfx:1.3.1
com.dlsc.unitfx:unitfx:1.0.10
com.fasterxml.jackson.core:jackson-annotations:2.17.2
@ -743,22 +717,21 @@ com.github.sialcasa.mvvmFX:mvvmfx-validation:f195849ca9
com.github.tomtung:latex2unicode_2.13:0.3.2
com.github.vatbub:mslinks:1.0.6.2
com.github.weisj:jsvg:1.2.0
com.google.code.gson:gson:2.11.0
com.google.errorprone:error_prone_annotations:2.27.0
com.google.code.gson:gson:2.10.1
com.google.errorprone:error_prone_annotations:2.26.1
com.google.guava:failureaccess:1.0.2
com.google.guava:guava:33.1.0-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:3.0.0
com.googlecode.javaewah:JavaEWAH:1.2.3
com.googlecode.plist:dd-plist:1.28
com.h2database:h2-mvstore:2.3.232
com.knuddels:jtokkit:1.1.0
com.kohlschutter.junixsocket:junixsocket-common:2.10.0
com.kohlschutter.junixsocket:junixsocket-core:2.10.0
com.kohlschutter.junixsocket:junixsocket-mysql:2.10.0
com.kohlschutter.junixsocket:junixsocket-native-common:2.10.0
com.konghq:unirest-java-core:4.4.4
com.konghq:unirest-modules-gson:4.4.4
com.h2database:h2-mvstore:2.2.224
com.kohlschutter.junixsocket:junixsocket-common:2.9.1
com.kohlschutter.junixsocket:junixsocket-core:2.9.1
com.kohlschutter.junixsocket:junixsocket-mysql:2.9.1
com.kohlschutter.junixsocket:junixsocket-native-common:2.9.1
com.konghq:unirest-java-core:4.4.0
com.konghq:unirest-modules-gson:4.4.0
com.oracle.ojdbc:ojdbc10:19.3.0.0
com.oracle.ojdbc:ons:19.3.0.0
com.oracle.ojdbc:osdt_cert:19.3.0.0
@ -766,6 +739,7 @@ com.oracle.ojdbc:osdt_core:19.3.0.0
com.oracle.ojdbc:simplefan:19.3.0.0
com.oracle.ojdbc:ucp:19.3.0.0
com.sun.istack:istack-commons-runtime:4.1.2
com.tobiasdiez:easybind:2.2.1-SNAPSHOT
com.vladsch.flexmark:flexmark-ext-emoji:0.64.8
com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.64.8
com.vladsch.flexmark:flexmark-ext-ins:0.64.8
@ -788,12 +762,12 @@ com.vladsch.flexmark:flexmark-util-visitor:0.64.8
com.vladsch.flexmark:flexmark-util:0.64.8
com.vladsch.flexmark:flexmark:0.64.8
commons-beanutils:commons-beanutils:1.9.4
commons-cli:commons-cli:1.9.0
commons-codec:commons-codec:1.17.1
commons-cli:commons-cli:1.8.0
commons-codec:commons-codec:1.17.0
commons-collections:commons-collections:3.2.2
commons-digester:commons-digester:2.1
commons-io:commons-io:2.16.1
commons-logging:commons-logging:1.3.4
commons-logging:commons-logging:1.3.2
commons-validator:commons-validator:1.8.0
de.rototor.jeuclid:jeuclid-core:3.1.11
de.rototor.snuggletex:snuggletex-core:1.3.0
@ -805,7 +779,6 @@ de.undercouch:citeproc-java:3.1.0
eu.lestard:doc-annotations:0.2
info.debatty:java-string-similarity:2.0.0
io.github.java-diff-utils:java-diff-utils:4.12
io.zonky.test:embedded-postgres:2.0.7
jakarta.activation:jakarta.activation-api:2.1.3
jakarta.annotation:jakarta.annotation-api:2.1.1
jakarta.inject:jakarta.inject-api:2.0.1
@ -815,31 +788,30 @@ jakarta.xml.bind:jakarta.xml.bind-api:4.0.2
javax.measure:unit-api:2.2
net.harawata:appdirs:1.2.2
net.java.dev.jna:jna-platform:5.13.0
net.java.dev.jna:jna:5.14.0
net.java.dev.jna:jna:5.13.0
net.jcip:jcip-annotations:1.0
net.jodah:typetools:0.6.1
net.synedra:validatorfx:0.5.0
one.jpro.jproutils:tree-showing:0.2.2
org.antlr:antlr4-runtime:4.13.2
org.apache.commons:commons-compress:1.27.1
org.antlr:antlr4-runtime:4.13.1
org.apache.commons:commons-csv:1.11.0
org.apache.commons:commons-lang3:3.17.0
org.apache.commons:commons-lang3:3.14.0
org.apache.commons:commons-text:1.12.0
org.apache.httpcomponents.client5:httpclient5:5.3.1
org.apache.httpcomponents.core5:httpcore5-h2:5.2.4
org.apache.httpcomponents.core5:httpcore5:5.2.4
org.apache.logging.log4j:log4j-api:2.24.0
org.apache.logging.log4j:log4j-to-slf4j:2.24.0
org.apache.logging.log4j:log4j-api:2.23.1
org.apache.logging.log4j:log4j-to-slf4j:2.23.1
org.apache.lucene:lucene-analysis-common:9.11.1
org.apache.lucene:lucene-core:9.11.1
org.apache.lucene:lucene-highlighter:9.11.1
org.apache.lucene:lucene-queries:9.11.1
org.apache.lucene:lucene-queryparser:9.11.1
org.apache.lucene:lucene-sandbox:9.11.1
org.apache.pdfbox:fontbox:3.0.3
org.apache.pdfbox:pdfbox-io:3.0.3
org.apache.pdfbox:pdfbox:3.0.3
org.apache.pdfbox:xmpbox:3.0.3
org.apache.pdfbox:fontbox:3.0.2
org.apache.pdfbox:pdfbox-io:3.0.2
org.apache.pdfbox:pdfbox:3.0.2
org.apache.pdfbox:xmpbox:3.0.2
org.apiguardian:apiguardian-api:1.1.2
org.bouncycastle:bcprov-jdk18on:1.78.1
org.checkerframework:checker-qual:3.42.0
@ -861,19 +833,18 @@ org.glassfish.hk2:osgi-resource-locator:1.0.3
org.glassfish.jaxb:jaxb-core:4.0.3
org.glassfish.jaxb:jaxb-runtime:4.0.3
org.glassfish.jaxb:txw2:4.0.3
org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.8
org.glassfish.jersey.core:jersey-client:3.1.8
org.glassfish.jersey.core:jersey-common:3.1.8
org.glassfish.jersey.core:jersey-server:3.1.8
org.glassfish.jersey.inject:jersey-hk2:3.1.8
org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.7
org.glassfish.jersey.core:jersey-client:3.1.7
org.glassfish.jersey.core:jersey-common:3.1.7
org.glassfish.jersey.core:jersey-server:3.1.7
org.glassfish.jersey.inject:jersey-hk2:3.1.7
org.jabref:afterburner.fx:2.0.0
org.jabref:easybind:2.2.1-SNAPSHOT
org.javassist:javassist:3.30.2-GA
org.jbibtex:jbibtex:1.0.20
org.jetbrains:annotations:24.0.1
org.jooq:jool:0.9.15
org.jsoup:jsoup:1.18.1
org.jspecify:jspecify:1.0.0
org.jsoup:jsoup:1.17.2
org.jspecify:jspecify:0.3.0
org.kordamp.ikonli:ikonli-bootstrapicons-pack:12.3.1
org.kordamp.ikonli:ikonli-core:12.3.1
org.kordamp.ikonli:ikonli-javafx:12.3.1
@ -883,23 +854,22 @@ org.kordamp.ikonli:ikonli-materialdesign2-pack:12.3.1
org.libreoffice:libreoffice:24.2.3
org.libreoffice:unoloader:24.2.3
org.mariadb.jdbc:mariadb-java-client:2.7.9
org.openjfx:javafx-base:23
org.openjfx:javafx-controls:23
org.openjfx:javafx-fxml:23
org.openjfx:javafx-graphics:23
org.openjfx:javafx-media:23
org.openjfx:javafx-swing:23
org.openjfx:javafx-web:23
org.postgresql:postgresql:42.7.4
org.openjfx:javafx-base:22.0.1
org.openjfx:javafx-controls:22.0.1
org.openjfx:javafx-fxml:22.0.1
org.openjfx:javafx-graphics:22.0.1
org.openjfx:javafx-media:22.0.1
org.openjfx:javafx-swing:22.0.1
org.openjfx:javafx-web:22.0.1
org.postgresql:postgresql:42.7.3
org.reactfx:reactfx:2.0-M5
org.scala-lang:scala-library:2.13.8
org.slf4j:jul-to-slf4j:2.0.16
org.slf4j:slf4j-api:2.0.16
org.slf4j:jul-to-slf4j:2.0.13
org.slf4j:slf4j-api:2.0.13
org.tinylog:slf4j-tinylog:2.7.0
org.tinylog:tinylog-api:2.7.0
org.tinylog:tinylog-impl:2.7.0
org.tukaani:xz:1.9
org.yaml:snakeyaml:2.3
org.yaml:snakeyaml:2.2
pt.davidafsilva.apple:jkeychain:1.1.0
tech.units:indriya:2.2
tech.uom.lib:uom-lib-common:2.2

@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=f397b287023acdba1e9f6fc5ea72d22dd63669d59ed4a289a29b1a76eee151c6
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

@ -1,14 +0,0 @@
{
"nixpkgs": {
"branch": "nixos-24.11",
"description": "Nix Packages collection",
"homepage": null,
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3f0a8ac25fb674611b98089ca3a5dd6480175751",
"sha256": "10i7fllqjzq171afzhdf2d9r1pk9irvmq5n55h92rc47vlaabvr4",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/3f0a8ac25fb674611b98089ca3a5dd6480175751.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
}
}

@ -1,198 +0,0 @@
# This file has been generated by Niv.
let
#
# The fetchers. fetch_<type> fetches specs of type <type>.
#
fetch_file = pkgs: name: spec:
let
name' = sanitizeName name + "-src";
in
if spec.builtin or true then
builtins_fetchurl { inherit (spec) url sha256; name = name'; }
else
pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
fetch_tarball = pkgs: name: spec:
let
name' = sanitizeName name + "-src";
in
if spec.builtin or true then
builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
else
pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
fetch_git = name: spec:
let
ref =
spec.ref or (
if spec ? branch then "refs/heads/${spec.branch}" else
if spec ? tag then "refs/tags/${spec.tag}" else
abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!"
);
submodules = spec.submodules or false;
submoduleArg =
let
nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0;
emptyArgWithWarning =
if submodules
then
builtins.trace
(
"The niv input \"${name}\" uses submodules "
+ "but your nix's (${builtins.nixVersion}) builtins.fetchGit "
+ "does not support them"
)
{ }
else { };
in
if nixSupportsSubmodules
then { inherit submodules; }
else emptyArgWithWarning;
in
builtins.fetchGit
({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg);
fetch_local = spec: spec.path;
fetch_builtin-tarball = name: throw
''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
$ niv modify ${name} -a type=tarball -a builtin=true'';
fetch_builtin-url = name: throw
''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
$ niv modify ${name} -a type=file -a builtin=true'';
#
# Various helpers
#
# https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
sanitizeName = name:
(
concatMapStrings (s: if builtins.isList s then "-" else s)
(
builtins.split "[^[:alnum:]+._?=-]+"
((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
)
);
# The set of packages used when specs are fetched using non-builtins.
mkPkgs = sources: system:
let
sourcesNixpkgs =
import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
hasThisAsNixpkgsPath = <nixpkgs> == ./.;
in
if builtins.hasAttr "nixpkgs" sources
then sourcesNixpkgs
else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
import <nixpkgs> { }
else
abort
''
Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
add a package called "nixpkgs" to your sources.json.
'';
# The actual fetching function.
fetch = pkgs: name: spec:
if ! builtins.hasAttr "type" spec then
abort "ERROR: niv spec ${name} does not have a 'type' attribute"
else if spec.type == "file" then fetch_file pkgs name spec
else if spec.type == "tarball" then fetch_tarball pkgs name spec
else if spec.type == "git" then fetch_git name spec
else if spec.type == "local" then fetch_local spec
else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
else if spec.type == "builtin-url" then fetch_builtin-url name
else
abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
# If the environment variable NIV_OVERRIDE_${name} is set, then use
# the path directly as opposed to the fetched source.
replace = name: drv:
let
saneName = stringAsChars (c: if (builtins.match "[a-zA-Z0-9]" c) == null then "_" else c) name;
ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
in
if ersatz == "" then drv else
# this turns the string into an actual Nix path (for both absolute and
# relative paths)
if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
# Ports of functions for older nix versions
# a Nix version of mapAttrs if the built-in doesn't exist
mapAttrs = builtins.mapAttrs or (
f: set: with builtins;
listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
);
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
range = first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1);
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
concatMapStrings = f: list: concatStrings (map f list);
concatStrings = builtins.concatStringsSep "";
# https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
optionalAttrs = cond: as: if cond then as else { };
# fetchTarball version that is compatible between all the versions of Nix
builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
let
inherit (builtins) lessThan nixVersion fetchTarball;
in
if lessThan nixVersion "1.12" then
fetchTarball ({ inherit url; } // (optionalAttrs (name != null) { inherit name; }))
else
fetchTarball attrs;
# fetchurl version that is compatible between all the versions of Nix
builtins_fetchurl = { url, name ? null, sha256 }@attrs:
let
inherit (builtins) lessThan nixVersion fetchurl;
in
if lessThan nixVersion "1.12" then
fetchurl ({ inherit url; } // (optionalAttrs (name != null) { inherit name; }))
else
fetchurl attrs;
# Create the final "sources" from the config
mkSources = config:
mapAttrs
(
name: spec:
if builtins.hasAttr "outPath" spec
then
abort
"The values in sources.json should not have an 'outPath' attribute"
else
spec // { outPath = replace name (fetch config.pkgs name spec); }
)
config.sources;
# The "config" used by the fetchers
mkConfig =
{ sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
, sources ? if sourcesFile == null then { } else builtins.fromJSON (builtins.readFile sourcesFile)
, system ? builtins.currentSystem
, pkgs ? mkPkgs sources system
}: rec {
# The sources, i.e. the attribute set of spec name to spec
inherit sources;
# The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
inherit pkgs;
};
in
mkSources (mkConfig { }) // { __functor = _: settings: mkSources (mkConfig settings); }

@ -100,12 +100,8 @@ recipeList:
# - org.openrewrite.staticanalysis.ExplicitInitialization
- org.openrewrite.java.migrate.io.ReplaceFileInOrOutputStreamFinalizeWithClose
- org.openrewrite.java.migrate.net.JavaNetAPIs
- org.openrewrite.java.migrate.net.URLConstructorsToURIRecipes
- org.openrewrite.java.migrate.util.OptionalNotEmptyToIsPresent
- org.openrewrite.java.migrate.util.OptionalNotPresentToIsEmpty
- org.openrewrite.java.migrate.util.SequencedCollection
- org.openrewrite.java.migrate.lang.StringFormatted
- org.openrewrite.java.migrate.util.SequencedCollection
- org.openrewrite.java.RemoveObjectsIsNull
- org.openrewrite.java.ShortenFullyQualifiedTypeReferences
@ -210,6 +206,3 @@ recipeList:
- org.openrewrite.java.testing.junit5.RemoveTryCatchFailBlocks
- org.openrewrite.java.testing.junit5.LifecycleNonPrivate
- org.openrewrite.java.testing.junit5.StaticImports
# Logging
- org.openrewrite.java.logging.slf4j.Slf4jBestPractices

@ -55,8 +55,7 @@ Then, everything is removed.
| [`fedora`](fedora/) | source | -- | -- | -- |
| [`Linux Mint (Cinnamon)`](linux-mint-cinnamon/) | source | Firefox | yes | -- |
| [`ubuntu`](ubuntu/) | snap | Firefox | yes | -- |
| [`windows 10`](windows10/) | source | Edge | -- | -- |
| [`windows 11`](windows11/) | source | Edge | -- | -- |
| [`windows`](windows/) | source | Edge | -- | -- |
## Troubleshooting

@ -1,4 +1,4 @@
# Windows 10 VM
# Windows VM
A Windows-based VM to test JabRef.
As user, you need to ensure to have the proper Windows license to use this VM.

@ -1,7 +1,7 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
NAME = "jabref-windows-10-sandbox"
NAME = "jabref-windows-sandbox"
Vagrant.configure("2") do |config|
config.vm.box = "gusztavvargadr/windows-10"

@ -1,49 +0,0 @@
# Windows 11 VM
A Windows-based VM to test JabRef.
As user, you need to ensure to have the proper Windows license to use this VM.
In case you have many CPU cores, you can adapt `vb.cpus` in `Vagrantfile` to a higher number.
One has to install the [JabRef Browser Extension](https://addons.mozilla.org/en-US/firefox/addon/jabref/) manually.
## Troubleshooting
### "Waiting for machine to reboot..."
In case Vagrant reports "Waiting for machine to reboot..." and nothing happens, one has to "power off" the machine, execute `vagrant destory`, and then run `vagrant up` again.
### `fatal: early EOF`
```console
jabref-windows-sandbox: Cloning into 'jabref'...
jabref-windows-sandbox: error: RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly: CANCEL (err 8)
jabref-windows-sandbox: error: 6846 bytes of body are still expected
jabref-windows-sandbox: fetch-pack: unexpected disconnect while reading sideband packet
jabref-windows-sandbox: fatal: early EOF
jabref-windows-sandbox: fatal: fetch-pack: invalid index-pack output
```
The `git clone` command did not work.
Login, open `cmd` and then execute following commands:
```cmd
git clone --recurse-submodules https://github.com/JabRef/jabref.git
cd jabref
gradlew run
```
## Background
`Vagrantfile` is based on [SeisoLLC/windows-sandbox](https://github.com/SeisoLLC/windows-sandbox/tree/main).
The most use image seems to be the [Windows 10 image by `gusztavvargadr`](https://portal.cloud.hashicorp.com/vagrant/discover/gusztavvargadr/windows-10).
List of all images at <https://portal.cloud.hashicorp.com/vagrant/discover/gusztavvargadr>.
[Chocolatey](https://chocolatey.org/) is used instead of [winget-cli](https://learn.microsoft.com/en-us/windows/package-manager/), because Chocolatey installation does not hit GitHub's rate limits during unattended installation.
## Atlernatives
- Atlernative Vagrant images: <https://app.vagrantup.com/boxes/search?q=windows+10&utf8=%E2%9C%93>.
- [Windows Sandbox](https://learn.microsoft.com/en-us/windows/security/application-security/application-isolation/windows-sandbox/windows-sandbox-overview)

@ -1,46 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
NAME = "jabref-windows-11-sandbox"
Vagrant.configure("2") do |config|
# config.vm.box = "Caden/windows-11-pro-jp-22h2-22621.1992"
config.vm.box = "stromweld/windows-11"
config.vm.define NAME
config.vm.hostname = NAME
config.vm.provider "virtualbox" do |vb|
vb.name = NAME
vb.memory = 6000
vb.cpus = 2
vb.customize ['modifyvm', :id, '--clipboard-mode', 'bidirectional']
vb.gui = true
end
config.vm.provision "shell", privileged: "true", powershell_elevated_interactive: "true", inline: <<-SHELL
# Install chocolatey
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
choco feature enable -n=allowGlobalConfirmation
choco install libericajdk
choco install git.install -y --params "/GitAndUnixToolsOnPath /WindowsTerminal /WindowsTerminalProfile"
# Required by AI functionaltiy
choco choco install vcredist140
# choco install firefox
# choco install libreoffice-fresh
SHELL
config.vm.provision "shell", reboot: true
config.vm.provision "shell", privileged: "false", inline: <<-SHELL
cd \\users\\vagrant
git clone --depth=1 --recurse-submodules https://github.com/JabRef/jabref.git
# cd jabref
# .\\gradlew jar
SHELL
end

@ -1,33 +0,0 @@
{ }:
let
### Import nixpkgs with Niv ###
#
sources = import ./nix/sources.nix;
# nixpkgs stable
pkgs = import sources.nixpkgs {
# Required for IntelliJ Ultimate
config.allowUnfree = true;
};
# https://github.com/NixOS/nixpkgs/blob/d9e98537533e7d978556bd58739813a47db5d591/pkgs/top-level/all-packages.nix#L14078-L14083
jdk = pkgs.jdk23.override {
enableJavaFX = true;
openjfx_jdk = pkgs.openjfx23.override { withWebKit = true; };
};
# https://github.com/NixOS/nixpkgs/blob/23e89b7da85c3640bbc2173fe04f4bd114342367/pkgs/development/tools/build-managers/gradle/default.nix#L46
gradle = pkgs.gradle.override { java = jdk; };
in
(
# Gradle's toolchain support does not work with IntelliJ, thus we have to use buildFHSUserEnv
pkgs.buildFHSUserEnv
{
name = "intellij-gradle-jdk23";
targetPkgs = pkgs_: [
pkgs_.jetbrains.idea-ultimate
jdk
gradle
];
}
).env

@ -58,7 +58,7 @@ environment:
parts:
jabref:
plugin: dump
source: https://builds.jabref.org/main/JabRef-6.0-portable_linux.tar.gz
source: https://builds.jabref.org/main/JabRef-5.16-portable_linux.tar.gz
stage-packages:
- x11-utils
override-build: |

@ -16,9 +16,7 @@ import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.importer.fileformat.BibtexParser;
import org.jabref.logic.layout.format.HTMLChars;
import org.jabref.logic.layout.format.LatexToUnicodeFormatter;
import org.jabref.logic.os.OS;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.preferences.JabRefCliPreferences;
import org.jabref.logic.util.OS;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.BibDatabaseMode;
@ -31,6 +29,8 @@ import org.jabref.model.groups.GroupHierarchyType;
import org.jabref.model.groups.KeywordGroup;
import org.jabref.model.groups.WordKeywordGroup;
import org.jabref.model.metadata.MetaData;
import org.jabref.preferences.JabRefPreferences;
import org.jabref.preferences.PreferencesService;
import com.airhacks.afterburner.injection.Injector;
import org.openjdk.jmh.Main;
@ -52,7 +52,7 @@ public class Benchmarks {
@Setup
public void init() throws Exception {
Injector.setModelOrService(CliPreferences.class, JabRefCliPreferences.getInstance());
Injector.setModelOrService(PreferencesService.class, JabRefPreferences.getInstance());
Random randomizer = new Random();
for (int i = 0; i < 1000; i++) {
@ -89,8 +89,8 @@ public class Benchmarks {
@Benchmark
public ParserResult parse() throws IOException {
CliPreferences preferences = Injector.instantiateModelOrService(CliPreferences.class);
BibtexParser parser = new BibtexParser(preferences.getImportFormatPreferences());
PreferencesService preferencesService = Injector.instantiateModelOrService(PreferencesService.class);
BibtexParser parser = new BibtexParser(preferencesService.getImportFormatPreferences());
return parser.parse(new StringReader(bibtexString));
}

@ -4,82 +4,49 @@
* These search expressions are used for searching the bibtex library. They are heavily used for search groups.
*/
grammar Search;
options { caseInsensitive = true; }
WS: [ \t\n\r]+ -> skip; // whitespace is ignored/skipped
WS: [ \t] -> skip; // whitespace is ignored/skipped
LPAREN: '(';
RPAREN: ')';
LPAREN:'(';
RPAREN:')';
EQUAL: '='; // case insensitive contains, semantically the same as CONTAINS
CEQUAL: '=!'; // case sensitive contains
EQUAL:'='; // semantically the same as CONTAINS
EEQUAL:'=='; // semantically the same as MATCHES
NEQUAL:'!=';
EEQUAL: '=='; // exact match case insensitive, semantically the same as MATCHES
CEEQUAL: '==!'; // exact match case sensitive
AND:[aA][nN][dD]; // 'and' case insensitive
OR:[oO][rR]; // 'or' case insensitive
CONTAINS:[cC][oO][nN][tT][aA][iI][nN][sS]; // 'contains' case insensitive
MATCHES:[mM][aA][tT][cC][hH][eE][sS]; // 'matches' case insensitive
NOT:[nN][oO][tT]; // 'not' case insensitive
REQUAL: '=~'; // regex check case insensitive
CREEQUAL: '=~!'; // regex check case sensitive
STRING:QUOTE (~'"')* QUOTE;
QUOTE:'"';
NEQUAL: '!='; // negated case insensitive contains
NCEQUAL: '!=!'; // negated case sensitive contains
FIELDTYPE:LETTER+;
// fragments are not accessible from the code, they are only for describing the grammar better
fragment LETTER : ~[ \t"()=!];
NEEQUAL: '!=='; // negated case insensitive exact match
NCEEQUAL: '!==!'; // negated case sensitive exact match
NREQUAL: '!=~'; // negated regex check case insensitive
NCREEQUAL: '!=~!'; // negated regex check case sensitive
start:
expression EOF;
AND: 'AND';
OR: 'OR';
CONTAINS: 'CONTAINS';
MATCHES: 'MATCHES';
NOT: 'NOT';
FIELD: [A-Z]+;
STRING_LITERAL: '"' ('\\"' | ~["])* '"'; // " should be escaped with a backslash
TERM: ('\\' [=!~()] | ~[ \t\n\r=!~()])+; // =!~() should be escaped with a backslash
start
: EOF
| andExpression EOF
// labels are used to refer to parts of the rules in the generated code later on
// label=actualThingy
expression:
LPAREN expression RPAREN #parenExpression // example: (author=miller)
| NOT expression #unaryExpression // example: not author = miller
| left=expression operator=AND right=expression #binaryExpression // example: author = miller and title = test
| left=expression operator=OR right=expression #binaryExpression // example: author = miller or title = test
| comparison #atomExpression
;
andExpression
: expression+ #implicitAndExpression // example: author = miller year = 2010 --> equivalent to: author = miller AND year = 2010
comparison:
left=name operator=(CONTAINS | MATCHES | EQUAL | EEQUAL | NEQUAL) right=name // example: author != miller
| right=name // example: miller (search all fields)
;
expression
: LPAREN andExpression RPAREN #parenExpression // example: (author = miller)
| NOT expression #negatedExpression // example: NOT author = miller
| left = expression bin_op = AND right = expression #binaryExpression // example: author = miller AND year = 2010
| left = expression bin_op = OR right = expression #binaryExpression // example: author = miller OR year = 2010
| comparison #comparisonExpression // example: miller OR author = miller
;
comparison
: FIELD operator searchValue // example: author = miller
| searchValue // example: miller
;
operator
: EQUAL
| CEQUAL
| EEQUAL
| CEEQUAL
| REQUAL
| CREEQUAL
| NEQUAL
| NCEQUAL
| NEEQUAL
| NCEEQUAL
| NREQUAL
| NCREEQUAL
| CONTAINS
| MATCHES
;
searchValue
: STRING_LITERAL
| FIELD
| TERM
name:
STRING // example: "miller"
| FIELDTYPE // example: author
;

@ -89,16 +89,14 @@ open module org.jabref {
// dependency injection using HK2
requires org.glassfish.hk2.api;
// region HTTP clients
requires org.apache.httpcomponents.core5.httpcore5;
requires org.jsoup;
// region: http clients
requires unirest.java.core;
requires unirest.modules.gson;
requires org.apache.httpcomponents.core5.httpcore5;
requires org.jsoup;
// endregion
// region: SQL databases
requires embedded.postgres;
requires org.tukaani.xz;
requires ojdbc10;
requires org.postgresql.jdbc;
requires org.mariadb.jdbc;
@ -110,7 +108,6 @@ open module org.jabref {
requires io.github.javadiffutils;
requires java.string.similarity;
requires org.apache.commons.cli;
requires org.apache.commons.compress;
requires org.apache.commons.csv;
requires org.apache.commons.io;
requires org.apache.commons.lang3;
@ -145,29 +142,22 @@ open module org.jabref {
requires org.jooq.jool;
// region AI
// region: AI
requires ai.djl.api;
requires ai.djl.pytorch_model_zoo;
requires ai.djl.tokenizers;
requires jvm.openai;
requires langchain4j;
requires langchain4j.core;
requires langchain4j.google.ai.gemini;
requires langchain4j.hugging.face;
requires langchain4j.mistral.ai;
requires langchain4j.open.ai;
uses ai.djl.engine.EngineProvider;
uses ai.djl.repository.RepositoryFactory;
uses ai.djl.repository.zoo.ZooProvider;
uses dev.langchain4j.spi.prompt.PromptTemplateFactory;
requires velocity.engine.core;
// endregion
// region: Lucene
/*
* In case the version is updated, please also increment {@link org.jabref.model.search.LinkedFilesConstants.VERSION} to trigger reindexing.
/**
* In case the version is updated, please also adapt {@link org.jabref.model.search.SearchFieldConstants#VERSION} to the newly used version.
*/
uses org.apache.lucene.codecs.lucene100.Lucene100Codec;
uses org.apache.lucene.codecs.lucene99.Lucene99Codec;
requires org.apache.lucene.analysis.common;
requires org.apache.lucene.core;
requires org.apache.lucene.highlighter;
@ -180,7 +170,7 @@ open module org.jabref {
requires org.eclipse.jgit;
uses org.eclipse.jgit.transport.SshSessionFactory;
uses org.eclipse.jgit.lib.Signer;
uses org.eclipse.jgit.lib.GpgSigner;
requires transitive org.jspecify;

@ -1,48 +1,233 @@
package org.jabref;
import java.io.File;
import java.io.IOException;
import java.net.Authenticator;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.jabref.cli.JabKit;
import org.jabref.cli.ArgumentProcessor;
import org.jabref.cli.JabRefCLI;
import org.jabref.gui.JabRefGUI;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.preferences.JabRefGuiPreferences;
import org.jabref.gui.util.DefaultDirectoryMonitor;
import org.jabref.gui.util.DefaultFileUpdateMonitor;
import org.jabref.logic.UiCommand;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.search.PostgreServer;
import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.net.ProxyAuthenticator;
import org.jabref.logic.net.ProxyPreferences;
import org.jabref.logic.net.ProxyRegisterer;
import org.jabref.logic.net.ssl.SSLPreferences;
import org.jabref.logic.net.ssl.TrustStoreManager;
import org.jabref.logic.protectedterms.ProtectedTermsLoader;
import org.jabref.logic.remote.RemotePreferences;
import org.jabref.logic.remote.client.RemoteClient;
import org.jabref.logic.util.BuildInfo;
import org.jabref.logic.util.HeadlessExecutorService;
import org.jabref.logic.util.OS;
import org.jabref.migrations.PreferencesMigrations;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.util.DirectoryMonitor;
import org.jabref.model.util.FileUpdateMonitor;
import org.jabref.preferences.JabRefPreferences;
import org.jabref.preferences.PreferencesService;
import org.jabref.preferences.ai.AiApiKeyProvider;
import com.airhacks.afterburner.injection.Injector;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
import org.tinylog.configuration.Configuration;
/// The main entry point for the JabRef application.
///
/// It has two main functions:
///
/// - Handle the command line arguments
/// - Start the JavaFX application (if not in CLI mode)
/**
* The main entry point for the JabRef application.
* <p>
* It has two main functions:
* - Handle the command line arguments
* - Start the JavaFX application (if not in cli mode)
*/
public class Launcher {
private static Logger LOGGER;
public static void main(String[] args) {
JabKit.initLogging(args);
initLogging(args);
// Initialize preferences
final JabRefGuiPreferences preferences = JabRefGuiPreferences.getInstance();
Injector.setModelOrService(CliPreferences.class, preferences);
Injector.setModelOrService(GuiPreferences.class, preferences);
try {
Injector.setModelOrService(BuildInfo.class, new BuildInfo());
DefaultFileUpdateMonitor fileUpdateMonitor = new DefaultFileUpdateMonitor();
HeadlessExecutorService.INSTANCE.executeInterruptableTask(fileUpdateMonitor, "FileUpdateMonitor");
// Initialize preferences
final JabRefPreferences preferences = JabRefPreferences.getInstance();
Injector.setModelOrService(PreferencesService.class, preferences);
Injector.setModelOrService(AiApiKeyProvider.class, preferences);
List<UiCommand> uiCommands = JabKit.processArguments(args, preferences, fileUpdateMonitor);
// The method `processArguments` quites the whole JVM if no GUI is needed.
// Early exit in case another instance is already running
if (!handleMultipleAppInstances(args, preferences.getRemotePreferences())) {
return;
}
PreferencesMigrations.runMigrations(preferences);
BibEntryTypesManager entryTypesManager = preferences.getCustomEntryTypesRepository();
Injector.setModelOrService(BibEntryTypesManager.class, entryTypesManager);
PostgreServer postgreServer = new PostgreServer();
Injector.setModelOrService(PostgreServer.class, postgreServer);
PreferencesMigrations.runMigrations(preferences, entryTypesManager);
JabRefGUI.setup(uiCommands, preferences, fileUpdateMonitor);
JabRefGUI.launch(JabRefGUI.class, args);
Injector.setModelOrService(JournalAbbreviationRepository.class, JournalAbbreviationLoader.loadRepository(preferences.getJournalAbbreviationPreferences()));
Injector.setModelOrService(ProtectedTermsLoader.class, new ProtectedTermsLoader(preferences.getProtectedTermsPreferences()));
configureProxy(preferences.getProxyPreferences());
configureSSL(preferences.getSSLPreferences());
clearOldSearchIndices();
try {
DefaultFileUpdateMonitor fileUpdateMonitor = new DefaultFileUpdateMonitor();
Injector.setModelOrService(FileUpdateMonitor.class, fileUpdateMonitor);
HeadlessExecutorService.INSTANCE.executeInterruptableTask(fileUpdateMonitor, "FileUpdateMonitor");
DirectoryMonitor directoryMonitor = new DefaultDirectoryMonitor();
Injector.setModelOrService(DirectoryMonitor.class, directoryMonitor);
// Process arguments
ArgumentProcessor argumentProcessor = new ArgumentProcessor(
args,
ArgumentProcessor.Mode.INITIAL_START,
preferences,
fileUpdateMonitor,
entryTypesManager);
argumentProcessor.processArguments();
if (argumentProcessor.shouldShutDown()) {
LOGGER.debug("JabRef shut down after processing command line arguments");
// A clean shutdown takes 60s time
// We don't need the clean shutdown here
System.exit(0);
}
List<UiCommand> uiCommands = new ArrayList<>(argumentProcessor.getUiCommands());
JabRefGUI.setup(uiCommands, preferences, fileUpdateMonitor);
JabRefGUI.launch(JabRefGUI.class, args);
} catch (ParseException e) {
LOGGER.error("Problem parsing arguments", e);
JabRefCLI.printUsage(preferences);
}
} catch (Exception ex) {
LOGGER.error("Unexpected exception", ex);
}
}
/**
* This needs to be called as early as possible. After the first log write, it
* is not possible to alter the log configuration programmatically anymore.
*/
private static void initLogging(String[] args) {
// routeLoggingToSlf4J
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
// We must configure logging as soon as possible, which is why we cannot wait for the usual
// argument parsing workflow to parse logging options .e.g. --debug
boolean isDebugEnabled;
try {
JabRefCLI jabRefCLI = new JabRefCLI(args);
isDebugEnabled = jabRefCLI.isDebugLogging();
} catch (ParseException e) {
isDebugEnabled = false;
}
// addLogToDisk
Path directory = OS.getNativeDesktop().getLogDirectory();
try {
Files.createDirectories(directory);
} catch (IOException e) {
LOGGER = LoggerFactory.getLogger(Launcher.class);
LOGGER.error("Could not create log directory {}", directory, e);
return;
}
// The "Shared File Writer" is explained at
// https://tinylog.org/v2/configuration/#shared-file-writer
Map<String, String> configuration = Map.of(
"level", isDebugEnabled ? "debug" : "info",
"writerFile", "rolling file",
"writerFile.level", isDebugEnabled ? "debug" : "info",
// We need to manually join the path, because ".resolve" does not work on Windows, because ":" is not allowed in file names on Windows
"writerFile.file", directory + File.separator + "log_{date:yyyy-MM-dd_HH-mm-ss}.txt",
"writerFile.charset", "UTF-8",
"writerFile.policies", "startup",
"writerFile.backups", "30");
configuration.forEach(Configuration::set);
LOGGER = LoggerFactory.getLogger(Launcher.class);
}
/**
* @return true if JabRef should continue starting up, false if it should quit.
*/
private static boolean handleMultipleAppInstances(String[] args, RemotePreferences remotePreferences) {
LOGGER.trace("Checking for remote handling...");
if (remotePreferences.useRemoteServer()) {
// Try to contact already running JabRef
RemoteClient remoteClient = new RemoteClient(remotePreferences.getPort());
if (remoteClient.ping()) {
LOGGER.debug("Pinging other instance succeeded.");
// We are not alone, there is already a server out there, send command line
// arguments to other instance
LOGGER.debug("Passing arguments passed on to running JabRef...");
if (remoteClient.sendCommandLineArguments(args)) {
// So we assume it's all taken care of, and quit.
// Output to both to the log and the screen. Therefore, we do not have an additional System.out.println.
LOGGER.info("Arguments passed on to running JabRef instance. Shutting down.");
return false;
} else {
LOGGER.warn("Could not communicate with other running JabRef instance.");
// We do not launch a new instance in presence of an error
return false;
}
} else {
LOGGER.debug("Could not ping JabRef instance.");
}
}
return true;
}
private static void configureProxy(ProxyPreferences proxyPreferences) {
ProxyRegisterer.register(proxyPreferences);
if (proxyPreferences.shouldUseProxy() && proxyPreferences.shouldUseAuthentication()) {
Authenticator.setDefault(new ProxyAuthenticator());
}
}
private static void configureSSL(SSLPreferences sslPreferences) {
TrustStoreManager.createTruststoreFileIfNotExist(Path.of(sslPreferences.getTruststorePath()));
}
private static void clearOldSearchIndices() {
Path currentIndexPath = OS.getNativeDesktop().getFulltextIndexBaseDirectory();
Path appData = currentIndexPath.getParent();
try {
Files.createDirectories(currentIndexPath);
} catch (IOException e) {
LOGGER.error("Could not create index directory {}", appData, e);
}
try (DirectoryStream<Path> stream = Files.newDirectoryStream(appData)) {
for (Path path : stream) {
if (Files.isDirectory(path) && !path.toString().endsWith("ssl") && path.toString().contains("lucene")
&& !path.equals(currentIndexPath)) {
LOGGER.info("Deleting out-of-date fulltext search index at {}.", path);
Files.walk(path)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}
} catch (IOException e) {
LOGGER.error("Could not access app-directory at {}", appData, e);
}
}
}

@ -13,7 +13,9 @@ import java.util.Optional;
import java.util.Set;
import java.util.prefs.BackingStoreException;
import org.jabref.logic.FilePreferences;
import org.jabref.gui.externalfiles.AutoSetFileLinksUtil;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.util.CurrentThreadTaskExecutor;
import org.jabref.logic.JabRefException;
import org.jabref.logic.UiCommand;
import org.jabref.logic.bibtex.FieldPreferences;
@ -40,12 +42,9 @@ import org.jabref.logic.importer.fileformat.BibtexParser;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.net.URLDownload;
import org.jabref.logic.os.OS;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.search.DatabaseSearcher;
import org.jabref.logic.search.SearchPreferences;
import org.jabref.logic.shared.prefs.SharedDatabasePreferences;
import org.jabref.logic.util.CurrentThreadTaskExecutor;
import org.jabref.logic.util.OS;
import org.jabref.logic.util.io.FileUtil;
import org.jabref.logic.xmp.XmpPreferences;
import org.jabref.model.database.BibDatabase;
@ -53,10 +52,13 @@ import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.search.query.SearchQuery;
import org.jabref.model.search.SearchQuery;
import org.jabref.model.strings.StringUtil;
import org.jabref.model.util.DummyFileUpdateMonitor;
import org.jabref.model.util.FileUpdateMonitor;
import org.jabref.preferences.FilePreferences;
import org.jabref.preferences.PreferencesService;
import org.jabref.preferences.SearchPreferences;
import com.airhacks.afterburner.injection.Injector;
import com.google.common.base.Throwables;
@ -68,11 +70,11 @@ public class ArgumentProcessor {
public enum Mode { INITIAL_START, REMOTE_START }
private final CliOptions cli;
private final JabRefCLI cli;
private final Mode startupMode;
private final CliPreferences cliPreferences;
private final PreferencesService preferencesService;
private final FileUpdateMonitor fileUpdateMonitor;
private final BibEntryTypesManager entryTypesManager;
@ -82,30 +84,31 @@ public class ArgumentProcessor {
/**
* First call the constructor, then call {@link #processArguments()}.
* Afterward, you can access the {@link #getUiCommands()}.
*
* @implNote both cli and gui preferences are passed to make the dependency to GUI parts explicit
*/
public ArgumentProcessor(String[] args,
Mode startupMode,
CliPreferences cliPreferences,
PreferencesService preferencesService,
FileUpdateMonitor fileUpdateMonitor,
BibEntryTypesManager entryTypesManager)
throws org.apache.commons.cli.ParseException {
this.cli = new CliOptions(args);
this.cli = new JabRefCLI(args);
this.startupMode = startupMode;
this.cliPreferences = cliPreferences;
this.preferencesService = preferencesService;
this.fileUpdateMonitor = fileUpdateMonitor;
this.entryTypesManager = entryTypesManager;
}
/**
* Will open a file (like {@link #importFile(String)}, but will also request JabRef to focus on this library.
* Will open a file (like importFile), but will also request JabRef to focus on this database
*
* @param argument See importFile.
* @return ParserResult with setToOpenTab(true)
*/
private Optional<ParserResult> importToOpenBase(String importArguments) {
Optional<ParserResult> result = importFile(importArguments);
private Optional<ParserResult> importToOpenBase(String argument) {
Optional<ParserResult> result = importFile(argument);
result.ifPresent(ParserResult::setToOpenTab);
return result;
}
@ -122,13 +125,9 @@ public class ArgumentProcessor {
}
}
/**
*
* @param importArguments Format: <code>fileName[,format]</code>
*/
private Optional<ParserResult> importFile(String importArguments) {
LOGGER.debug("Importing file {}", importArguments);
String[] data = importArguments.split(",");
private Optional<ParserResult> importFile(String argument) {
LOGGER.debug("Importing file {}", argument);
String[] data = argument.split(",");
String address = data[0];
Path file;
@ -167,9 +166,9 @@ public class ArgumentProcessor {
private Optional<ParserResult> importFile(Path file, String importFormat) {
try {
ImportFormatReader importFormatReader = new ImportFormatReader(
cliPreferences.getImporterPreferences(),
cliPreferences.getImportFormatPreferences(),
cliPreferences.getCitationKeyPatternPreferences(),
preferencesService.getImporterPreferences(),
preferencesService.getImportFormatPreferences(),
preferencesService.getCitationKeyPatternPreferences(),
fileUpdateMonitor
);
@ -201,7 +200,7 @@ public class ArgumentProcessor {
}
if ((startupMode == Mode.INITIAL_START) && cli.isHelp()) {
CliOptions.printUsage(cliPreferences);
JabRefCLI.printUsage(preferencesService);
guiNeeded = false;
return;
}
@ -238,6 +237,10 @@ public class ArgumentProcessor {
regenerateCitationKeys(loaded);
}
if (cli.isAutomaticallySetFileLinks()) {
automaticallySetFileLinks(loaded);
}
if ((cli.isWriteXmpToPdf() && cli.isEmbedBibFileInPdf()) || (cli.isWriteMetadataToPdf() && (cli.isWriteXmpToPdf() || cli.isEmbedBibFileInPdf()))) {
System.err.println("Give only one of [writeXmpToPdf, embedBibFileInPdf, writeMetadataToPdf]");
}
@ -246,11 +249,11 @@ public class ArgumentProcessor {
if (!loaded.isEmpty()) {
writeMetadataToPdf(loaded,
cli.getWriteMetadataToPdf(),
cliPreferences.getXmpPreferences(),
cliPreferences.getFilePreferences(),
cliPreferences.getLibraryPreferences().getDefaultBibDatabaseMode(),
cliPreferences.getCustomEntryTypesRepository(),
cliPreferences.getFieldPreferences(),
preferencesService.getXmpPreferences(),
preferencesService.getFilePreferences(),
preferencesService.getLibraryPreferences().getDefaultBibDatabaseMode(),
Injector.instantiateModelOrService(BibEntryTypesManager.class),
preferencesService.getFieldPreferences(),
Injector.instantiateModelOrService(JournalAbbreviationRepository.class),
cli.isWriteXmpToPdf() || cli.isWriteMetadataToPdf(),
cli.isEmbedBibFileInPdf() || cli.isWriteMetadataToPdf());
@ -268,7 +271,7 @@ public class ArgumentProcessor {
if (cli.isPreferencesExport()) {
try {
cliPreferences.exportPreferences(Path.of(cli.getPreferencesExport()));
preferencesService.exportPreferences(Path.of(cli.getPreferencesExport()));
} catch (JabRefException ex) {
LOGGER.error("Cannot export preferences", ex);
}
@ -291,8 +294,8 @@ public class ArgumentProcessor {
}
}
private static void writeMetadataToPdf(List<ParserResult> loaded,
String filesAndCiteKeys,
private void writeMetadataToPdf(List<ParserResult> loaded,
String filesAndCitekeys,
XmpPreferences xmpPreferences,
FilePreferences filePreferences,
BibDatabaseMode databaseMode,
@ -311,7 +314,7 @@ public class ArgumentProcessor {
XmpPdfExporter xmpPdfExporter = new XmpPdfExporter(xmpPreferences);
EmbeddedBibFilePdfExporter embeddedBibFilePdfExporter = new EmbeddedBibFilePdfExporter(databaseMode, entryTypesManager, fieldPreferences);
if ("all".equals(filesAndCiteKeys)) {
if ("all".equals(filesAndCitekeys)) {
for (BibEntry entry : databaseContext.getEntries()) {
writeMetadataToPDFsOfEntry(
databaseContext,
@ -329,7 +332,7 @@ public class ArgumentProcessor {
List<String> citeKeys = new ArrayList<>();
List<String> pdfs = new ArrayList<>();
for (String fileOrCiteKey : filesAndCiteKeys.split(",")) {
for (String fileOrCiteKey : filesAndCitekeys.split(",")) {
if (fileOrCiteKey.toLowerCase(Locale.ROOT).endsWith(".pdf")) {
pdfs.add(fileOrCiteKey);
} else {
@ -357,7 +360,7 @@ public class ArgumentProcessor {
embeddBibfile);
}
private static void writeMetadataToPDFsOfEntry(BibDatabaseContext databaseContext,
private void writeMetadataToPDFsOfEntry(BibDatabaseContext databaseContext,
String citeKey,
BibEntry entry,
FilePreferences filePreferences,
@ -365,7 +368,7 @@ public class ArgumentProcessor {
EmbeddedBibFilePdfExporter embeddedBibFilePdfExporter,
JournalAbbreviationRepository abbreviationRepository,
boolean writeXMP,
boolean embedBibfile) {
boolean embeddBibfile) {
try {
if (writeXMP) {
if (xmpPdfExporter.exportToAllFilesOfEntry(databaseContext, filePreferences, entry, List.of(entry), abbreviationRepository)) {
@ -374,7 +377,7 @@ public class ArgumentProcessor {
System.err.printf("Cannot write XMP metadata on any linked files of %s. Make sure there is at least one linked file and the path is correct.%n", citeKey);
}
}
if (embedBibfile) {
if (embeddBibfile) {
if (embeddedBibFilePdfExporter.exportToAllFilesOfEntry(databaseContext, filePreferences, entry, List.of(entry), abbreviationRepository)) {
System.out.printf("Successfully embedded metadata on at least one linked file of %s%n", citeKey);
} else {
@ -386,7 +389,7 @@ public class ArgumentProcessor {
}
}
private static void writeMetadataToPdfByCitekey(BibDatabaseContext databaseContext,
private void writeMetadataToPdfByCitekey(BibDatabaseContext databaseContext,
List<String> citeKeys,
FilePreferences filePreferences,
XmpPdfExporter xmpPdfExporter,
@ -406,7 +409,7 @@ public class ArgumentProcessor {
}
}
private static void writeMetadataToPdfByFileNames(BibDatabaseContext databaseContext,
private void writeMetadataToPdfByFileNames(BibDatabaseContext databaseContext,
List<String> pdfs,
FilePreferences filePreferences,
XmpPdfExporter xmpPdfExporter,
@ -453,13 +456,12 @@ public class ArgumentProcessor {
ParserResult pr = loaded.getLast();
BibDatabaseContext databaseContext = pr.getDatabaseContext();
SearchPreferences searchPreferences = cliPreferences.getSearchPreferences();
SearchPreferences searchPreferences = preferencesService.getSearchPreferences();
SearchQuery query = new SearchQuery(searchTerm, searchPreferences.getSearchFlags());
List<BibEntry> matches;
try {
// extract current thread task executor from indexManager
matches = new DatabaseSearcher(query, databaseContext, new CurrentThreadTaskExecutor(), cliPreferences).getMatches();
matches = new DatabaseSearcher(query, databaseContext, new CurrentThreadTaskExecutor(), preferencesService.getFilePreferences()).getMatches();
} catch (IOException e) {
LOGGER.error("Error occurred when searching", e);
return false;
@ -477,7 +479,7 @@ public class ArgumentProcessor {
formatName = "bib";
default -> {
System.err.println(Localization.lang("Output file missing").concat(". \n \t ")
.concat(Localization.lang("Usage")).concat(": ") + CliOptions.getExportMatchesSyntax());
.concat(Localization.lang("Usage")).concat(": ") + JabRefCLI.getExportMatchesSyntax());
guiNeeded = false;
return false;
}
@ -490,7 +492,9 @@ public class ArgumentProcessor {
LOGGER.debug("Finished export");
} else {
// export new database
ExporterFactory exporterFactory = ExporterFactory.create(cliPreferences);
ExporterFactory exporterFactory = ExporterFactory.create(
preferencesService,
Injector.instantiateModelOrService(BibEntryTypesManager.class));
Optional<Exporter> exporter = exporterFactory.getExporterByName(formatName);
if (exporter.isEmpty()) {
System.err.println(Localization.lang("Unknown export format %0", formatName));
@ -549,7 +553,7 @@ public class ArgumentProcessor {
try {
pr = OpenDatabase.loadDatabase(
Path.of(aLeftOver),
cliPreferences.getImportFormatPreferences(),
preferencesService.getImportFormatPreferences(),
fileUpdateMonitor);
// In contrast to org.jabref.gui.LibraryTab.onDatabaseLoadingSucceed, we do not execute OpenDatabaseAction.performPostOpenActions(result, dialogService);
} catch (IOException ex) {
@ -589,7 +593,7 @@ public class ArgumentProcessor {
}
if (!cli.isBlank() && cli.isBibtexImport()) {
importBibtexToOpenBase(cli.getBibtexImport(), cliPreferences.getImportFormatPreferences()).ifPresent(loaded::add);
importBibtexToOpenBase(cli.getBibtexImport(), preferencesService.getImportFormatPreferences()).ifPresent(loaded::add);
}
return loaded;
@ -625,12 +629,12 @@ public class ArgumentProcessor {
try (AtomicFileWriter fileWriter = new AtomicFileWriter(Path.of(subName), StandardCharsets.UTF_8)) {
BibWriter bibWriter = new BibWriter(fileWriter, OS.NEWLINE);
SelfContainedSaveConfiguration saveConfiguration = (SelfContainedSaveConfiguration) new SelfContainedSaveConfiguration()
.withReformatOnSave(cliPreferences.getLibraryPreferences().shouldAlwaysReformatOnSave());
.withReformatOnSave(preferencesService.getLibraryPreferences().shouldAlwaysReformatOnSave());
BibDatabaseWriter databaseWriter = new BibtexDatabaseWriter(
bibWriter,
saveConfiguration,
cliPreferences.getFieldPreferences(),
cliPreferences.getCitationKeyPatternPreferences(),
preferencesService.getFieldPreferences(),
preferencesService.getCitationKeyPatternPreferences(),
entryTypesManager);
databaseWriter.saveDatabase(new BibDatabaseContext(newBase));
@ -666,9 +670,11 @@ public class ArgumentProcessor {
BibDatabaseContext databaseContext = parserResult.getDatabaseContext();
databaseContext.setDatabasePath(path);
List<Path> fileDirForDatabase = databaseContext
.getFileDirectories(cliPreferences.getFilePreferences());
.getFileDirectories(preferencesService.getFilePreferences());
System.out.println(Localization.lang("Exporting %0", data[0]));
ExporterFactory exporterFactory = ExporterFactory.create(cliPreferences);
ExporterFactory exporterFactory = ExporterFactory.create(
preferencesService,
Injector.instantiateModelOrService(BibEntryTypesManager.class));
Optional<Exporter> exporter = exporterFactory.getExporterByName(data[1]);
if (exporter.isEmpty()) {
System.err.println(Localization.lang("Unknown export format %0", data[1]));
@ -690,8 +696,8 @@ public class ArgumentProcessor {
private void importPreferences() {
try {
cliPreferences.importPreferences(Path.of(cli.getPreferencesImport()));
Injector.setModelOrService(BibEntryTypesManager.class, cliPreferences.getCustomEntryTypesRepository());
preferencesService.importPreferences(Path.of(cli.getPreferencesImport()));
Injector.setModelOrService(BibEntryTypesManager.class, preferencesService.getCustomEntryTypesRepository());
} catch (JabRefException ex) {
LOGGER.error("Cannot import preferences", ex);
}
@ -701,7 +707,7 @@ public class ArgumentProcessor {
if ("all".equals(value.trim())) {
try {
System.out.println(Localization.lang("Setting all preferences to default values."));
cliPreferences.clear();
preferencesService.clear();
new SharedDatabasePreferences().clear();
} catch (BackingStoreException e) {
System.err.println(Localization.lang("Unable to clear preferences."));
@ -711,7 +717,7 @@ public class ArgumentProcessor {
String[] keys = value.split(",");
for (String key : keys) {
try {
cliPreferences.deleteKey(key.trim());
preferencesService.deleteKey(key.trim());
System.out.println(Localization.lang("Resetting preference key '%0'", key.trim()));
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
@ -720,6 +726,18 @@ public class ArgumentProcessor {
}
}
private void automaticallySetFileLinks(List<ParserResult> loaded) {
for (ParserResult parserResult : loaded) {
BibDatabase database = parserResult.getDatabase();
LOGGER.info(Localization.lang("Automatically setting file links"));
AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(
parserResult.getDatabaseContext(),
preferencesService.getFilePreferences(),
preferencesService.getAutoLinkPreferences());
util.linkAssociatedFiles(database.getEntries(), new NamedCompound(""));
}
}
private void regenerateCitationKeys(List<ParserResult> loaded) {
for (ParserResult parserResult : loaded) {
BibDatabase database = parserResult.getDatabase();
@ -728,7 +746,7 @@ public class ArgumentProcessor {
CitationKeyGenerator keyGenerator = new CitationKeyGenerator(
parserResult.getDatabaseContext(),
cliPreferences.getCitationKeyPatternPreferences());
preferencesService.getCitationKeyPatternPreferences());
for (BibEntry entry : database.getEntries()) {
keyGenerator.generateAndSetKey(entry);
}
@ -753,8 +771,8 @@ public class ArgumentProcessor {
String query = split[1];
Set<SearchBasedFetcher> fetchers = WebFetchers.getSearchBasedFetchers(
cliPreferences.getImportFormatPreferences(),
cliPreferences.getImporterPreferences());
preferencesService.getImportFormatPreferences(),
preferencesService.getImporterPreferences());
Optional<SearchBasedFetcher> selectedFetcher = fetchers.stream()
.filter(fetcher -> fetcher.getName().equalsIgnoreCase(engine))
.findFirst();

@ -2,9 +2,9 @@ package org.jabref.cli;
import java.nio.file.Path;
import org.jabref.gui.auximport.AuxParserResultViewModel;
import org.jabref.logic.auxparser.AuxParser;
import org.jabref.logic.auxparser.AuxParserResult;
import org.jabref.logic.auxparser.AuxParserStatisticsProvider;
import org.jabref.logic.auxparser.DefaultAuxParser;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.strings.StringUtil;
@ -26,7 +26,7 @@ public class AuxCommandLine {
AuxParserResult result = auxParser.parse(Path.of(auxFile));
subDatabase = result.getGeneratedBibDatabase();
// print statistics
System.out.println(new AuxParserStatisticsProvider(result).getInformation(true));
System.out.println(new AuxParserResultViewModel(result).getInformation(true));
}
return subDatabase;
}

@ -1,241 +0,0 @@
package org.jabref.cli;
import java.io.File;
import java.io.IOException;
import java.net.Authenticator;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.jabref.logic.UiCommand;
import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.net.ProxyAuthenticator;
import org.jabref.logic.net.ProxyPreferences;
import org.jabref.logic.net.ProxyRegisterer;
import org.jabref.logic.net.ssl.SSLPreferences;
import org.jabref.logic.net.ssl.TrustStoreManager;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.preferences.JabRefCliPreferences;
import org.jabref.logic.protectedterms.ProtectedTermsLoader;
import org.jabref.logic.remote.RemotePreferences;
import org.jabref.logic.remote.client.RemoteClient;
import org.jabref.logic.util.BuildInfo;
import org.jabref.logic.util.Directories;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.util.DummyFileUpdateMonitor;
import org.jabref.model.util.FileUpdateMonitor;
import com.airhacks.afterburner.injection.Injector;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
import org.tinylog.configuration.Configuration;
/// Entrypoint for a command-line only version of JabRef.
/// It does not open any dialogs, just parses the command line arguments and outputs text and creates/modifies files.
///
/// See [Command Line Interface Guidelines](https://clig.dev/) for general guidelines how to design a good CLI interface.
///
/// It does not open any GUI.
/// For the GUI application see {@link org.jabref.Launcher}.
///
/// Does not do any preference migrations.
public class JabKit {
private static Logger LOGGER;
public static void main(String[] args) {
initLogging(args);
final JabRefCliPreferences preferences = JabRefCliPreferences.getInstance();
Injector.setModelOrService(CliPreferences.class, preferences);
FileUpdateMonitor fileUpdateMonitor = new DummyFileUpdateMonitor();
List<UiCommand> uiCommands = processArguments(args, preferences, fileUpdateMonitor);
if (!uiCommands.isEmpty()) {
LOGGER.error("No GUI needed, but UI commands were returned. Exiting.");
}
}
private static void systemExit() {
LOGGER.debug("JabRef shut down after processing command line arguments");
// A clean shutdown takes 60s time
// We don't need the clean shutdown here
System.exit(0);
}
public static List<UiCommand> processArguments(String[] args, JabRefCliPreferences preferences, FileUpdateMonitor fileUpdateMonitor) {
try {
Injector.setModelOrService(BuildInfo.class, new BuildInfo());
// Early exit in case another instance is already running
if (!handleMultipleAppInstances(args, preferences.getRemotePreferences())) {
systemExit();
}
BibEntryTypesManager entryTypesManager = preferences.getCustomEntryTypesRepository();
Injector.setModelOrService(BibEntryTypesManager.class, entryTypesManager);
Injector.setModelOrService(JournalAbbreviationRepository.class, JournalAbbreviationLoader.loadRepository(preferences.getJournalAbbreviationPreferences()));
Injector.setModelOrService(ProtectedTermsLoader.class, new ProtectedTermsLoader(preferences.getProtectedTermsPreferences()));
configureProxy(preferences.getProxyPreferences());
configureSSL(preferences.getSSLPreferences());
clearOldSearchIndices();
try {
Injector.setModelOrService(FileUpdateMonitor.class, fileUpdateMonitor);
// Process arguments
ArgumentProcessor argumentProcessor = new ArgumentProcessor(
args,
ArgumentProcessor.Mode.INITIAL_START,
preferences,
fileUpdateMonitor,
entryTypesManager);
argumentProcessor.processArguments();
if (argumentProcessor.shouldShutDown()) {
LOGGER.debug("JabRef shut down after processing command line arguments");
// A clean shutdown takes 60s time
// We don't need the clean shutdown here
System.exit(0);
return null;
}
return new ArrayList<>(argumentProcessor.getUiCommands());
} catch (ParseException e) {
LOGGER.error("Problem parsing arguments", e);
CliOptions.printUsage(preferences);
systemExit();
return null;
}
} catch (Exception ex) {
LOGGER.error("Unexpected exception", ex);
systemExit();
return null;
}
}
/**
* This needs to be called as early as possible. After the first log write, it
* is not possible to alter the log configuration programmatically anymore.
*/
public static void initLogging(String[] args) {
// routeLoggingToSlf4J
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
// We must configure logging as soon as possible, which is why we cannot wait for the usual
// argument parsing workflow to parse logging options .e.g. --debug
boolean isDebugEnabled;
try {
CliOptions cliOptions = new CliOptions(args);
isDebugEnabled = cliOptions.isDebugLogging();
} catch (ParseException e) {
isDebugEnabled = false;
}
// addLogToDisk
// We cannot use `Injector.instantiateModelOrService(BuildInfo.class).version` here, because this initializes logging
Path directory = Directories.getLogDirectory(new BuildInfo().version);
try {
Files.createDirectories(directory);
} catch (IOException e) {
LOGGER = LoggerFactory.getLogger(JabKit.class);
LOGGER.error("Could not create log directory {}", directory, e);
return;
}
// The "Shared File Writer" is explained at
// https://tinylog.org/v2/configuration/#shared-file-writer
Map<String, String> configuration = Map.of(
"level", isDebugEnabled ? "debug" : "info",
"writerFile", "rolling file",
"writerFile.level", isDebugEnabled ? "debug" : "info",
// We need to manually join the path, because ".resolve" does not work on Windows, because ":" is not allowed in file names on Windows
"writerFile.file", directory + File.separator + "log_{date:yyyy-MM-dd_HH-mm-ss}.txt",
"writerFile.charset", "UTF-8",
"writerFile.policies", "startup",
"writerFile.backups", "30");
configuration.forEach(Configuration::set);
LOGGER = LoggerFactory.getLogger(JabKit.class);
}
/**
* @return true if JabRef should continue starting up, false if it should quit.
*/
private static boolean handleMultipleAppInstances(String[] args, RemotePreferences remotePreferences) throws InterruptedException {
LOGGER.trace("Checking for remote handling...");
if (remotePreferences.useRemoteServer()) {
// Try to contact already running JabRef
RemoteClient remoteClient = new RemoteClient(remotePreferences.getPort());
if (remoteClient.ping()) {
LOGGER.debug("Pinging other instance succeeded.");
if (args.length == 0) {
// There is already a server out there, avoid showing log "Passing arguments" while no arguments are provided.
LOGGER.warn("This JabRef instance is already running. Please switch to that instance.");
} else {
// We are not alone, there is already a server out there, send command line arguments to other instance
LOGGER.debug("Passing arguments passed on to running JabRef...");
if (remoteClient.sendCommandLineArguments(args)) {
// So we assume it's all taken care of, and quit.
// Output to both to the log and the screen. Therefore, we do not have an additional System.out.println.
LOGGER.info("Arguments passed on to running JabRef instance. Shutting down.");
} else {
LOGGER.warn("Could not communicate with other running JabRef instance.");
}
}
// We do not launch a new instance in presence if there is another instance running
return false;
} else {
LOGGER.debug("Could not ping JabRef instance.");
}
}
return true;
}
private static void configureProxy(ProxyPreferences proxyPreferences) {
ProxyRegisterer.register(proxyPreferences);
if (proxyPreferences.shouldUseProxy() && proxyPreferences.shouldUseAuthentication()) {
Authenticator.setDefault(new ProxyAuthenticator());
}
}
private static void configureSSL(SSLPreferences sslPreferences) {
TrustStoreManager.createTruststoreFileIfNotExist(Path.of(sslPreferences.getTruststorePath()));
}
private static void clearOldSearchIndices() {
Path currentIndexPath = Directories.getFulltextIndexBaseDirectory();
Path appData = currentIndexPath.getParent();
try {
Files.createDirectories(currentIndexPath);
} catch (IOException e) {
LOGGER.error("Could not create index directory {}", appData, e);
}
try (DirectoryStream<Path> stream = Files.newDirectoryStream(appData)) {
for (Path path : stream) {
if (Files.isDirectory(path) && !path.toString().endsWith("ssl") && path.toString().contains("lucene")
&& !path.equals(currentIndexPath)) {
LOGGER.info("Deleting out-of-date fulltext search index at {}.", path);
Files.walk(path)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}
} catch (IOException e) {
LOGGER.error("Could not access app-directory at {}", appData, e);
}
}
}

@ -8,12 +8,14 @@ import javafx.util.Pair;
import org.jabref.logic.exporter.ExporterFactory;
import org.jabref.logic.importer.ImportFormatReader;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.os.OS;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.util.BuildInfo;
import org.jabref.logic.util.OS;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.strings.StringUtil;
import org.jabref.model.util.DummyFileUpdateMonitor;
import org.jabref.preferences.PreferencesService;
import com.airhacks.afterburner.injection.Injector;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
@ -21,21 +23,18 @@ import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
/**
* Holds the command line options. It parses it using Apache Commons CLI.
*/
public class CliOptions {
public class JabRefCLI {
private static final int WIDTH = 100; // Number of characters per line before a line break must be added.
private static final String WRAPPED_LINE_PREFIX = ""; // If a line break is added, this prefix will be inserted at the beginning of the next line
private static final String STRING_TABLE_DELIMITER = " : ";
private final CommandLine commandLine;
private final CommandLine cl;
private final List<String> leftOver;
public CliOptions(String[] args) throws ParseException {
public JabRefCLI(String[] args) throws ParseException {
Options options = getOptions();
this.commandLine = new DefaultParser().parse(options, args, true);
this.leftOver = commandLine.getArgList();
this.cl = new DefaultParser().parse(options, args, true);
this.leftOver = cl.getArgList();
}
public static String getExportMatchesSyntax() {
@ -46,133 +45,137 @@ public class CliOptions {
}
public boolean isHelp() {
return commandLine.hasOption("help");
return cl.hasOption("help");
}
public boolean isShowVersion() {
return commandLine.hasOption("version");
return cl.hasOption("version");
}
public boolean isBlank() {
return commandLine.hasOption("blank");
return cl.hasOption("blank");
}
public boolean isDisableGui() {
return commandLine.hasOption("nogui");
return cl.hasOption("nogui");
}
public boolean isPreferencesExport() {
return commandLine.hasOption("prexp");
return cl.hasOption("prexp");
}
public String getPreferencesExport() {
return commandLine.getOptionValue("prexp", "jabref_prefs.xml");
return cl.getOptionValue("prexp", "jabref_prefs.xml");
}
public boolean isPreferencesImport() {
return commandLine.hasOption("primp");
return cl.hasOption("primp");
}
public String getPreferencesImport() {
return commandLine.getOptionValue("primp", "jabref_prefs.xml");
return cl.getOptionValue("primp", "jabref_prefs.xml");
}
public boolean isPreferencesReset() {
return commandLine.hasOption("prdef");
return cl.hasOption("prdef");
}
public String getPreferencesReset() {
return commandLine.getOptionValue("prdef");
return cl.getOptionValue("prdef");
}
public boolean isFileExport() {
return commandLine.hasOption("output");
return cl.hasOption("output");
}
public String getFileExport() {
return commandLine.getOptionValue("output");
return cl.getOptionValue("output");
}
public boolean isBibtexImport() {
return commandLine.hasOption("importBibtex");
return cl.hasOption("importBibtex");
}
public String getBibtexImport() {
return commandLine.getOptionValue("importBibtex");
return cl.getOptionValue("importBibtex");
}
public boolean isFileImport() {
return commandLine.hasOption("import");
return cl.hasOption("import");
}
public String getFileImport() {
return commandLine.getOptionValue("import");
return cl.getOptionValue("import");
}
public boolean isAuxImport() {
return commandLine.hasOption("aux");
return cl.hasOption("aux");
}
public String getAuxImport() {
return commandLine.getOptionValue("aux");
return cl.getOptionValue("aux");
}
public boolean isImportToOpenBase() {
return commandLine.hasOption("importToOpen");
return cl.hasOption("importToOpen");
}
public String getImportToOpenBase() {
return commandLine.getOptionValue("importToOpen");
return cl.getOptionValue("importToOpen");
}
public boolean isDebugLogging() {
return commandLine.hasOption("debug");
return cl.hasOption("debug");
}
public boolean isFetcherEngine() {
return commandLine.hasOption("fetch");
return cl.hasOption("fetch");
}
public String getFetcherEngine() {
return commandLine.getOptionValue("fetch");
return cl.getOptionValue("fetch");
}
public boolean isExportMatches() {
return commandLine.hasOption("exportMatches");
return cl.hasOption("exportMatches");
}
public String getExportMatches() {
return commandLine.getOptionValue("exportMatches");
return cl.getOptionValue("exportMatches");
}
public boolean isGenerateCitationKeys() {
return commandLine.hasOption("generateCitationKeys");
return cl.hasOption("generateCitationKeys");
}
public boolean isAutomaticallySetFileLinks() {
return cl.hasOption("automaticallySetFileLinks");
}
public boolean isWriteXmpToPdf() {
return commandLine.hasOption("writeXmpToPdf");
return cl.hasOption("writeXmpToPdf");
}
public boolean isEmbedBibFileInPdf() {
return commandLine.hasOption("embedBibFileInPdf");
return cl.hasOption("embedBibFileInPdf");
}
public boolean isWriteMetadataToPdf() {
return commandLine.hasOption("writeMetadataToPdf");
return cl.hasOption("writeMetadataToPdf");
}
public String getWriteMetadataToPdf() {
return commandLine.hasOption("writeMetadatatoPdf") ? commandLine.getOptionValue("writeMetadataToPdf") :
commandLine.hasOption("writeXMPtoPdf") ? commandLine.getOptionValue("writeXmpToPdf") :
commandLine.hasOption("embeddBibfileInPdf") ? commandLine.getOptionValue("embeddBibfileInPdf") : null;
return cl.hasOption("writeMetadatatoPdf") ? cl.getOptionValue("writeMetadataToPdf") :
cl.hasOption("writeXMPtoPdf") ? cl.getOptionValue("writeXmpToPdf") :
cl.hasOption("embeddBibfileInPdf") ? cl.getOptionValue("embeddBibfileInPdf") : null;
}
public String getJumpToKey() {
return commandLine.getOptionValue("jumpToKey");
return cl.getOptionValue("jumpToKey");
}
public boolean isJumpToKey() {
return commandLine.hasOption("jumpToKey");
return cl.hasOption("jumpToKey");
}
private static Options getOptions() {
@ -181,6 +184,7 @@ public class CliOptions {
// boolean options
options.addOption("h", "help", false, Localization.lang("Display help on command line options"));
options.addOption("n", "nogui", false, Localization.lang("No GUI. Only process command line options"));
options.addOption("asfl", "automaticallySetFileLinks", false, Localization.lang("Automatically set file links"));
options.addOption("g", "generateCitationKeys", false, Localization.lang("Regenerate all keys for the entries in a BibTeX file"));
options.addOption("b", "blank", false, Localization.lang("Do not open any files at startup"));
options.addOption("v", "version", false, Localization.lang("Display version"));
@ -207,7 +211,7 @@ public class CliOptions {
.longOpt("importBibtex")
.desc("%s: '%s'".formatted(Localization.lang("Import BibTeX"), "-ib @article{entry}"))
.hasArg()
.argName("BIBTEX_STRING")
.argName("BIBTEXT_STRING")
.build());
options.addOption(Option
@ -305,13 +309,13 @@ public class CliOptions {
System.out.println(getVersionInfo());
}
public static void printUsage(CliPreferences preferences) {
public static void printUsage(PreferencesService preferencesService) {
String header = "";
ImportFormatReader importFormatReader = new ImportFormatReader(
preferences.getImporterPreferences(),
preferences.getImportFormatPreferences(),
preferences.getCitationKeyPatternPreferences(),
preferencesService.getImporterPreferences(),
preferencesService.getImportFormatPreferences(),
preferencesService.getCitationKeyPatternPreferences(),
new DummyFileUpdateMonitor()
);
List<Pair<String, String>> importFormats = importFormatReader
@ -321,7 +325,9 @@ public class CliOptions {
String importFormatsIntro = Localization.lang("Available import formats");
String importFormatsList = "%s:%n%s%n".formatted(importFormatsIntro, alignStringTable(importFormats));
ExporterFactory exporterFactory = ExporterFactory.create(preferences);
ExporterFactory exporterFactory = ExporterFactory.create(
preferencesService,
Injector.instantiateModelOrService(BibEntryTypesManager.class));
List<Pair<String, String>> exportFormats = exporterFactory
.getExporters().stream()
.map(format -> new Pair<>(format.getName(), format.getId()))
@ -336,7 +342,8 @@ public class CliOptions {
}
private String getVersionInfo() {
return "JabRef %s".formatted(new BuildInfo().version);
BuildInfo buildInfo = Injector.instantiateModelOrService(BuildInfo.class);
return "JabRef %s".formatted(buildInfo.version);
}
public List<String> getLeftOver() {

@ -1,5 +1,4 @@
.root {
/* Note that counting for odd/even starts at 0, thus the first displayed row is even */
-jr-row-odd-background: -fx-control-inner-background-alt;
-jr-row-even-background: -fx-control-inner-background;
/*
@ -341,6 +340,7 @@ TextFlow > .hyperlink:visited,
-fx-underline: true;
}
.glyph-icon {
/* This adjusts text alignment within the bounds of text nodes so that
the text is always vertically centered within the bounds. Based on
@ -1427,15 +1427,17 @@ We want to have a look that matches our icons in the tool-bar */
-fx-label-padding: 5 0 10 10;
}
.chips-pane > .editor {
-fx-pref-height: 30px;
-fx-padding: 0px 0px 0px -8px;
-fx-margin: 0em;
-fx-border-style: none;
}
.tags-field {
-fx-pref-height: 30px;
-fx-margin: 0em;
-fx-border-style: none;
-fx-background-color: -fx-outer-border, -fx-control-inner-background;
}
.tags-field:focused {
-fx-border-color: -jr-accent;
}
.tags-field > .flow-pane > .tag-view {
@ -1450,9 +1452,6 @@ We want to have a look that matches our icons in the tool-bar */
.tags-field-editor {
-fx-border-width: 0;
-fx-text-fill: -fx-focused-text-base-color;
-fx-highlight-text-fill: -fx-text-inner-color;
-fx-highlight-fill: derive(-jr-accent, 20%);
}
.searchBar:invalid {
@ -1517,219 +1516,4 @@ We want to have a look that matches our icons in the tool-bar */
-fx-background-color: transparent;
}
/* endregion */
/* region: maintable css */
.main-table .column-icon {
-fx-alignment: baseline-center;
-fx-padding: 0;
}
.main-table .column-header.column-icon > .label {
-fx-padding: 0;
-fx-alignment: baseline-center;
}
.main-table .empty-special-field {
visibility: hidden;
}
.main-table .table-row-cell:hover .empty-special-field {
visibility: visible;
-fx-icon-color: -jr-gray-2;
-fx-fill: -jr-gray-2;
}
.main-table .table-row-cell:dragOver-bottom {
-fx-border-color: -jr-drag-target;
-fx-border-width: 0 0 2 0;
-fx-padding: 0 0 -2 0;
}
.main-table .table-row-cell:dragOver-center {
-fx-border-color: -jr-drag-target;
-fx-border-width: 1 1 1 1;
-fx-padding: -1 -1 -1 -1;
-fx-background-color: -jr-drag-target-hover;
}
.main-table .table-row-cell:dragOver-top {
-fx-border-color: -jr-drag-target;
-fx-border-width: 2 0 0 0;
-fx-padding: -2 0 0 0;
}
/** even and odd are swapped around somehow. Below "odd" matches lines 2, 4, ... **/
.main-table .table-row-cell:matching-search-and-groups {
-fx-background-color: -jr-match-1-even;
}
.main-table .table-row-cell:matching-search-and-groups > .table-cell {
-fx-text-fill: -jr-match-1-text-color;
}
.main-table .table-row-cell:matching-search-and-groups:focused > .table-cell {
-fx-text-fill: -fx-focused-text-base-color;
}
.main-table .table-row-cell:matching-search-and-groups:focused:hover > .table-cell {
-fx-text-fill: -jr-maintable-focused-hover-text;
}
.main-table .table-row-cell:matching-search-and-groups:focused:hover > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-maintable-focused-hover-text;
}
.main-table .table-row-cell:matching-search-and-groups:hover > .table-cell {
-fx-text-fill: -jr-hover-text;
}
.main-table .table-row-cell:matching-search-and-groups > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-match-1-text-color;
}
.main-table .table-row-cell:matching-search-and-groups:hover > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-hover-text;
}
.main-table .table-row-cell:matching-search-and-groups:odd {
-fx-background-color: -jr-match-1-odd;
}
.main-table .table-row-cell:matching-search-and-groups:selected,
.main-table .table-row-cell:matching-search-and-groups:focused,
.main-table .table-row-cell:matching-search-and-groups:focused:hover {
-fx-background-color: -jr-selected;
}
.main-table .table-row-cell:matching-search-and-groups:hover {
-fx-background-color: -jr-hover;
}
.main-table .table-row-cell:matching-search-not-groups {
-fx-background-color: -jr-match-2-even;
}
.main-table .table-row-cell:matching-search-not-groups > .table-cell {
-fx-text-fill: -jr-match-2-text-color;
}
.main-table .table-row-cell:matching-search-not-groups:focused > .table-cell {
-fx-text-fill: -fx-focused-text-base-color;
}
.main-table .table-row-cell:matching-search-not-groups:focused:hover > .table-cell {
-fx-text-fill: -jr-maintable-focused-hover-text;
}
.main-table .table-row-cell:matching-search-not-groups:focused:hover > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-maintable-focused-hover-text;
}
.main-table .table-row-cell:matching-search-not-groups:hover > .table-cell {
-fx-text-fill: -jr-hover-text;
}
.main-table .table-row-cell:matching-search-not-groups > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-match-2-text-color;
}
.main-table .table-row-cell:matching-search-not-groups:hover > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-hover-text;
}
.main-table .table-row-cell:matching-search-not-groups:odd {
-fx-background-color: -jr-match-2-odd;
}
.main-table .table-row-cell:matching-search-not-groups:selected,
.main-table .table-row-cell:matching-search-not-groups:focused,
.main-table .table-row-cell:matching-search-not-groups:focused:hover,
.main-table .table-row-cell:matching-search-not-groups:hover {
-fx-background-color: -jr-selected;
}
.main-table .table-row-cell:matching-search-not-groups:hover {
-fx-background-color: -jr-hover;
}
.main-table .table-row-cell:matching-groups-not-search {
-fx-background-color: -jr-match-3-even;
}
.main-table .table-row-cell:matching-groups-not-search > .table-cell {
-fx-text-fill: -jr-match-3-text-color;
}
.main-table .table-row-cell:matching-groups-not-search:focused > .table-cell {
-fx-text-fill: -fx-focused-text-base-color;
}
.main-table .table-row-cell:matching-groups-not-search:focused:hover > .table-cell {
-fx-text-fill: -jr-maintable-focused-hover-text;
}
.main-table .table-row-cell:matching-groups-not-search:focused:hover > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-maintable-focused-hover-text;
}
.main-table .table-row-cell:matching-groups-not-search:hover > .table-cell {
-fx-text-fill: -jr-hover-text;
}
.main-table .table-row-cell:matching-groups-not-search > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-match-3-text-color;
}
.main-table .table-row-cell:matching-groups-not-search:hover > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-hover-text;
}
.main-table .table-row-cell:matching-groups-not-search:odd {
-fx-background-color: -jr-match-3-odd;
}
.main-table .table-row-cell:matching-groups-not-search:selected,
.main-table .table-row-cell:matching-groups-not-search:focused,
.main-table .table-row-cell:matching-groups-not-search:focused:hover,
.main-table .table-row-cell:matching-groups-not-search:hover {
-fx-background-color: -jr-selected;
}
.main-table .table-row-cell:matching-groups-not-search:hover {
-fx-background-color: -jr-hover;
}
.main-table .table-row-cell:not-matching-search-and-groups {
-fx-background-color: -jr-match-4-even;
}
.main-table .table-row-cell:not-matching-search-and-groups > .table-cell {
-fx-text-fill: -jr-match-4-text-color;
}
.main-table .table-row-cell:not-matching-search-and-groups:focused > .table-cell {
-fx-text-fill: -fx-focused-text-base-color;
}
.main-table .table-row-cell:not-matching-search-and-groups:focused:hover > .table-cell {
-fx-text-fill: -jr-maintable-focused-hover-text;
}
.main-table .table-row-cell:not-matching-search-and-groups:focused:hover > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-maintable-focused-hover-text;
}
.main-table .table-row-cell:not-matching-search-and-groups:hover > .table-cell {
-fx-text-fill: -jr-hover-text;
}
.main-table .table-row-cell:not-matching-search-and-groups > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-match-4-text-color;
}
.main-table.table-row-cell:not-matching-search-and-groups:hover > .table-cell > .ikonli-font-icon {
-fx-icon-color: -jr-hover-text;
}
.main-table .table-row-cell:not-matching-search-and-groups:odd {
-fx-background-color: -jr-match-4-odd;
}
.main-table .table-row-cell:not-matching-search-and-groups:selected,
.main-table .table-row-cell:not-matching-search-and-groups:focused,
.main-table .table-row-cell:not-matching-search-and-groups:focused:hover {
-fx-background-color: -jr-selected;
}
.main-table .table-row-cell:not-matching-search-and-groups:hover {
-fx-background-color: -jr-hover;
}
.rating > .container {
-fx-spacing: 2;
}
.rating > .container > .button {
-fx-pref-width: 16;
-fx-pref-height: 10;
-fx-background-repeat: no-repeat no-repeat;
-fx-background-size: 16 16;
-fx-border-style: none;
-fx-border-width: 0;
-fx-padding: 0;
}
.rating > .container > .button.strong {
}
.rating > .container > .button:hover {
-fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.6), 8, 0.0, 0, 0);
}
/* endregion */

Some files were not shown because too many files have changed in this diff Show More