diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 9d7234fb089..f875ffff259 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -21,4 +21,10 @@ * [Readings on Coding](readings-on-coding/README.md) * [Readings on JavaFX](readings-on-coding/javafx.md) * [Useful development tooling](readings-on-coding/tools.md) - +* [The OpenOffice/LibreOffice panel](openoffice/README.md) + * [Overview](openoffice/overview.md) + * [Order of appearance of citation groups](openoffice/order-of-appearance.md) + * [Problems](openoffice/problems.md) + * [Code reorganization](openoffice/code-reorganization.md) + * [About `OOError`, `OOResult` and `OOVoidResult`](openoffice/ooresult-ooerror.md) + * [Alternatives to using OOResult and OOVoidResult in OOBibBase](openoffice/ooresult-alternatives.md) diff --git a/docs/openoffice/README.md b/docs/openoffice/README.md new file mode 100644 index 00000000000..5587cc8291e --- /dev/null +++ b/docs/openoffice/README.md @@ -0,0 +1 @@ +# OpenOffice/LibreOffice integration diff --git a/docs/openoffice/code-reorganization.md b/docs/openoffice/code-reorganization.md new file mode 100644 index 00000000000..a9d45418690 --- /dev/null +++ b/docs/openoffice/code-reorganization.md @@ -0,0 +1,188 @@ + +# Code reorganization + +Why + +- Separate backend +- Separate GUI code (dialogs) and logic +- Data is now organized around `Citation`, `CitationGroup` instead of arrays for citation group + fields, and arrays of arrays for citation fields. + Also take `citationKey` as the central data unit, this is what we start with: unresolved `citationKeys` + do not stop processing. Although we cannot sort them by author and year, we can still emit a marker + that acts as a placeholder and shows the user the problematic key. + +## Result + +### Layers + +![Layers](layers-v1.svg) + +### By directories + +- `model` + - `util` : general utilities + - (`OOPair`, `OOTuple3`) collect two or three objects without creating a new class + - `OOResult` : while an Optional.empty can comunicate failure, it cannot provide details. + `OOResult` allows an arbitrary error object to be provided in case of failure. + - `OOVoidResult` : for functions returning no result on success, only diagnostics on failure. + - `OOListUtil`: some utilities working on List + - `uno` : helpers for various tasks via UNO. + These are conceptually independent of JabRef code and logic. + - `ootext` : to separate decisions on the format of references and citation marks from + the actual insertion into the document, the earlier method + [OOUtil.insertOOFormattedTextAtCurrentLocation](https://github.com/JabRef/jabref/blob/475b2989ffa8ec61c3327c62ed8f694149f83220/src/main/java/org/jabref/logic/openoffice/OOUtil.java#L112) + was extended to handle new tags that describe actions earlier done in code. + - This became [OOTextIntoOO.write](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/model/openoffice/ootext/OOTextIntoOO.java#L149) + - `(change)` Now all output to the document goes through this, not only those from Layout. This allows the citation markers and `jstyle:Title` to use these tags. + - This allows some backward-compatible extensions to jstyle. + `(change)` [Added](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/logic/openoffice/style/OOBibStyle.java#L92) + some extra keywords, in `{prefix}_MARKUP_BEFORE`, `{prefix}_MARKUP_AFTER` pairs to allow bracketing some parts of citation marks with text and/or open/close tag pairs. + - [OOFormat](https://github.com/antalk2/jabref/blob/improve-reversibility-rebased-03/src/main/java/org/jabref/model/openoffice/ootext/OOFormat.java) + contains helpers to create the appropriate tags + - [OOText](https://github.com/antalk2/jabref/blob/improve-reversibility-rebased-03/src/main/java/org/jabref/model/openoffice/ootext/OOText.java) formalizes + the distinction from `String`. I did not change `String` to `OOText` in old code, (in particular in OOStyle). + - `rangesort` : ordering objects that have an `XTextRange`, optionally with an extra integer to break ties. + - `RangeSort.partitionAndSortRanges` : since `XTextRangeCompare` can only compare `XTextRange` values in + the same `XText`, we partition them accordingly and only sort within each partiion. + - `RangeSortable` (interface), `RangeSortEntry` (implements) : + When we replace `XTextRange` of citation marks in footnotes with the range of the footnote mark, + multiple citation marks may be mapped to the same location. To preserve the order between these, + `RangeSortable` allows this order to be indicated by returning appropriate indices from `getIndexInPosition` + - `RangeSortVisual` : sort in top-to-bottom left-to-right order. + Needs a functional `XTextViewCursor`. + Works on `RangeSortable` values. + - `FunctionalTextViewCursor` : helper to get a functional `XTextViewCursor` (cannot always) + - `RangeOverlapWithin` : check for overlaps within a set of `XTextRange` values. Probably O(n*log(n)). Used for all-to-all check of protected ranges. + - `RangeOverlapBetween` : check for overlaps between two sets of `XTextRange` values. Assumes one set is small. O(n*k). + Used for checking if the cursor is in a protected range. + - `backend` : interfaces to be provided by backends. + May change as new backends may need different APIs. + - `style` : data structures and interfaces used while going from ordered list of citation groups + to formatted citation markers and bibliography. Does not communicate with the document. Too long to fit here, starting a new section. + +## model/style + +At the core, + +- we have `Citation` values + - represented in the document by their `citationKey` + - each may have a `pageInfo` +- A citation group (`CitationGroup`) has + - a list of citations (`citationsInStorageOrder`) + - an identifier `CitationGroupId cgid` + - this allows to refer to the group + - also used to associate the group to its citation markers location (outside the style part, + in [Backend](https://github.com/antalk2/jabref/blob/fed0952cbdaf7a76bcb09b3db5ac48f34f5ca388/src/main/java/org/jabref/logic/openoffice/backend/Backend52.java#L46)) + - `OODataModel dataModel` is here, in order to handle old (Jabref5.2) structure where pageInfo belonged to + CitationGroup not Citation + - `referenceMarkNameForLinking` is optional: can be used to crosslink to the citation marker + from the bibliography. +- `CitationGroups` represents the collection of citation groups. +Processing starts with creating a `CitationGroups` instance from the data stored in the document. + +- `CitedKey` represents a cited source, with ordered backreferences (using `CitationPath`) to the correponding +citations. + +- `CitedKeys` is just an order-preserving collection of `CitedKeys` that also supports lookup by +`citationKey`. While producing citation markers, we also create a corresponding `CitedKeys` +instance, and store it in `CitationGroups.bibliography`. This is already sorted, its entries have +`uniqueLetter` or `number` assigned, but not converted to markup yet. + +Common processing steps: + +- We need `globalOrder` for the citation groups (provided externally) +`CitationGroups.setGlobalOrder()` +- We need to look up each citationKey in the bibliography databases: + - `CitationGroups.lookupCitations` collects the cited keys, + looks up each, then distributes the results to the citations. + Uses a temporary `CitedKeys` instance, based on unsorted citations and citation groups. +- `CitationGroups.imposeLocalOrder` fills `localOrder` in each `CitationGroup` + +- Now we have order of appearance for the citations (`globalOrder` and `localOrder`). + We can create a `CitedKeys` instance (`bibliography`) according to this order. + +- For citations numbered in order of first appearance we number the sources and distribute the numbers +to the corresponding citations. +- For citations numbered in order of bibliography, we sort the bibliography, number, distribute. + +- For author-year citations we have to decide on the letters `uniqueLetter` used to distinguish +sources. This needs order of first appearance of the sources and recognizing clashing citation markers. +This is done in logic, in [`OOProcessAuthorYearMarkers.createUniqueLetters()`](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/logic/openoffice/style/OOProcessAuthorYearMarkers.java#L49) +- We also mark first appearance of each source ([`setIsFirstAppearanceOfSourceInCitations`](https://github.com/antalk2/jabref/blob/fed0952cbdaf7a76bcb09b3db5ac48f34f5ca388/src/main/java/org/jabref/logic/openoffice/style/OOProcessAuthorYearMarkers.java#L146)) + +The entry point for this processing is: [`OOProcess.produceCitationMarkers`](https://github.com/antalk2/jabref/blob/fed0952cbdaf7a76bcb09b3db5ac48f34f5ca388/src/main/java/org/jabref/logic/openoffice/style/OOProcess.java#L69). +It fills + +- each `CitationGroup.citationMarker` +- `CitationGroups.bibliography` + - From bibliography `OOFormatBibliography.formatBibliography()` creates an `OOText` + ready to be written to the document. + + +## logic/style + +- `StyleLoader` : not changed (knows about default styles) Used by GUI +- `OOPreFormatter` : LaTeX code to unicode and OOText tags. (not changed) +- `OOBibStyle` : is mostly concerned by loading/parsing jstyle files and presenting its pieces +to the rest. Originally it also contains code to format numeric and author-year citation markers. + - Details of their new implementations are in + [`OOBibStyleGetNumCitationMarker`](https://github.com/antalk2/jabref/blob/improve-reversibility-rebased-03/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetNumCitationMarker.java) and + [`OOBibStyleGetCitationMarker`](https://github.com/antalk2/jabref/blob/improve-reversibility-rebased-03/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java) + - The new implementations + - support pageInfo for each citation + - support unresolved citations + - instead of `List` and (`List` plus arrays and database) they expect + more self-contained entries `List`, `List`. + - We have distinct methods for `getNormalizedCitationMarker(CitationMarkerNormEntry)` and + `getNumCitationMarkerForBibliography(CitationMarkerNumericBibEntry)`. + - The corresponding interfaces in model/style: + - `CitationMarkerNumericEntry` + - `CitationMarkerEntry` + - `CitationMarkerNumericBibEntry` + - `CitationMarkerNormEntry` + describe their expected input entries. +- [`OOProcess.produceCitationMarkers`](https://github.com/antalk2/jabref/blob/fed0952cbdaf7a76bcb09b3db5ac48f34f5ca388/src/main/java/org/jabref/logic/openoffice/style/OOProcess.java#L69) +is the main entry point for style application. Calls to specific implementations +in `OOProcessCitationKeyMarkers`, `OOProcessNumericMarkers` and `OOProcessAuthorYearMarkers` +according to jstyle flags. + +## logic/backend + +Details of encoding and retrieving data stored in a document as well as +the citation maker locations. Also contains dataModel-dependent code +(which could probably be moved out once the datamodel is settled). + +Creating and finding the bibliography (providing a cursor to write at) should be here too. +These are currently in `UpdateBibliography` + +## logic/frontend + +- `OOFrontend` : has a `Backend` and `CitationGroups` + - Its constructor creates a backend, reads data from the document and creates a CitationGroups instance. + - provides functionality that requires both access to the document and the CitationGroups instance +- `RangeForOverlapCheck` used in `OOFrontend` +- `UpdateBibliography` : Create, find and update the bibliography in the document using output from + `produceCitationMarkers()` +- `UpdateCitationMarkers` create `CitationGroup`, update citation markers using output from + `produceCitationMarkers()` + + +## logic/action + +GUI-independent part of implementations of GUI actions. + +## gui + +- `OOError` : common error messages and dialog titles + - adds `title` to `Jabrefexception` + - converts from some common exception types using type-specific message + - contains some dialog messages that do not correspond to exceptions + +- `OOBibBase2` : most activity was moved out from here to parts discussed above. + - connecting / selecting a document moved to `OOBibBaseConnect` + - the rest connects higher parts of the GUI to actions in logic + - does argument and precondition checking + - catches all exceptions + - shows error and warning dialogs + - adds `enterUndoContext`, `leaveUndoContext` around action code + diff --git a/docs/openoffice/layers-v1.svg b/docs/openoffice/layers-v1.svg new file mode 100644 index 00000000000..f9b8239d504 --- /dev/null +++ b/docs/openoffice/layers-v1.svg @@ -0,0 +1,915 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + document content (UNO) + + frontend + + actions + + OOBibBase2 + + backend + + style + + OOTextIntoOO + + rangesort + data in doc, ranges + order ranges + fill ranges + markup text + XTextDocument + Backend, CitationGroups + + + + GUI: + BibEntry, BibDatabase, OOBibStyle + provides input in terms of these types + + provides connection to doc + Cite, Update, Merge, Separate, Manage, Export + Connect + Load Style + Create OOFrontend instance + Catch exceptions, Undo + Forward requests to actions + Check preconditions + + locations + citation keys + pageInfo + citation type + + + lookup, localOrder, number, + uniqueLetter, sort bibliography, + format citationMarkers, + format bibliography + + + or visually + within XText + + checkRangeOverlaps, checkRangeOverlapsWithCursor + connects the parts below + getVisuallySortedCitationGroups, imposeGlobalOrder + UpdateCitationMarkers, UpdateBibliography + lock screen refresh + GUI-independent part of actions + + + diff --git a/docs/openoffice/ooresult-alternatives.md b/docs/openoffice/ooresult-alternatives.md new file mode 100644 index 00000000000..27d5dbb7e11 --- /dev/null +++ b/docs/openoffice/ooresult-alternatives.md @@ -0,0 +1,505 @@ + +# Alternatives to `OOResult` during precondition checking in `OOBibBase` + +(Talk about ADRs prompted me to think about alternatives to what I used.) + +Situation: + +- some tests return no data, only report problems +- we may need to get some resources that might not be available + (for example: connection to a document, a functional textview cursor) +- some test depend on these resources + + +One strategy could be to use a single try-catch around the whole body, +then showing a message based on the type of exceptions thrown. + +## [base case] + +```java +try { + A a = f(); + B b = g(a); + realAction(a,b); +} catch (FirstExceptionType ex) { + showDialog( title, messageForFirstExceptionType(ex) ); +} catch (SecondExceptionType ex) { + showDialog( title, messageForSecondExceptionType(ex) ); +} catch (Exception ex) { + showDialog( title, messageForOtherExceptions(ex) ); +} +``` + +This our base case. + +It is not clear from the code, nor within the catch branches (unless +we start looking into stack traces) which call (`f()`, `g(a)` or +`realAction(a,b)`) resulted in the exception. This limits the +specificity of the message and makes it hard to think about the "why" +can we get this exception here? + +## Catch around each call? + +A more detailed strategy would be to try-catch around each call. +In case we need a result from the call, this means either increasingly indented +code (try-in-try). + +```java +try { + A a = f(); + try { + B b = g(a); + try { + realAction(ab); + } catch (...){ + showDialog(); + } + } catch (G ex) { + showDialog(title, ex); // title describes which GUI action we are in + } +} catch (F ex) { + // could an F be thrown in g? + showDialog( title, ex ); +} +``` + +or (declare and fill later) + +```java +A a = null; +try { + a = f(); +} catch (F ex) { + showDialog(title, ex); + return; +} +B b = null; +try { + b = g(a); +} catch (G ex) { + showDialog(title, ex); + return; +} +try { + realAction(ab); +} catch (...){ + showDialog(); +} +``` + +In either case, the code becomes littered with exception handling code. + +## Catch in wrappers? + +We might push the try-catch into its own function. +If the wrapper is called multiple times, this may reduce duplication of the +catch-and-assign-message part. + +We can show an error dialog here: `title` carries some information +from the caller, the exeption caught brings some from below. + +We still need to notify the action handler (the caller) about +failure. Since we have shown the dialog, we do not need to provide a +message. + +### Notify caller with `Optional` result + +With `Optional` we get something like this: + +#### [dialog in wrap, return Optional] + +```java +Optional wrap_f(String title) { + try { + return Optional.of(f()); + } catch (F ex) { + showDialog(title, ex); + return Optional.empty(); + } +} + +Optional wrap_g(String title, A a) { + try { + return Optional.of(g(a)); + } catch (G ex) { + showDialog(title, ex); + return Optional.empty(); + } +} +``` + +and use it like this: + +```java +Optional a = wrap_f(title); +if (a.isEmpty()) { return; } + +Optional b = wrap_g(title, a.get()); +if (b.isEmpty()) { return; } + +try { + realAction(a.get(), b.get()); +} catch (...) { +} +``` + +This looks fairly regular. + +If `g` did not need `a`, we could simplify to + +```java +Optional a = wrap_f(title); +Optional b = wrap_g(title); +if (a.isEmpty() || b.isEmpty()) { return; } + +try { + realAction(a.get(), b.get()); +} catch (...) { +} +``` + +### Notify caller with `Result` result + +With `Result` we get something like this: + +#### [dialog in wrap, return OOResult] + +```java +OOResult wrap_f() { + try { + return OOResult.ok(f()); + } catch (F ex) { + return OOResult.error(OOError.from(ex)); + } catch (F2 ex) { + String message = "..."; + return OOResult.error(new OOError(message, ex)); // [1] + } +} +// [1] : this OOError constructor (explicit message but no title) is missing + +Optional wrap_g(A a) { + try { + return OOResult.ok(g(a)); + } catch (G ex) { + return OOResult.error(OOError.from(ex)); + } +} +``` + +and use it like this: + +```java +OOResult a = wrap_f(); +if (testDialog(title, a)) { // [1] + return; +} + +// [1] needs boolean testDialog(String title, OOResultLike... a); +// where OOResultLike is an interface with `OOVoidResult asVoidResult()` +// and is implemented by OOResult and OOVoidResult + +OOResult b = wrap_g(a.get()); +if (testDialog(title, b)) { return; } // (checkstyle makes this 3 lines) + +try { + realAction(a.get(), b.get()); +} catch (...) { +} +``` + +If `g` did not need `a`, we could simplify to + +```java +Optional a = wrap_f(); +Optional b = wrap_g(); +if (testDialog(title, a, b)) { // a single dialog can show both messages + return; +} + +try { + realAction(a.get(), b.get()); +} catch (...) { +} +``` + +### Notify caller by throwing an exception + +Or we can throw an exception to notify the caller. + +To simplify code in the caller, I assume we are using an exception +type not used elsewhere, but shared by all precondition checks. + +#### [dialog in wrap, PreconditionException] + +```java +A wrap_f(String title) throws PreconditionException { + try { + return f(); + } catch (F ex) { + showDialog(title, ex) + throw new PreconditionException(); + } +} + +B wrap_g(String title, A a) throws PreconditionException { + try { + return g(a); + } catch (G ex) { + showDialog(title, ex); + throw new PreconditionException(); + } +} +``` + +use + +```java +try { + A a = wrap_f(title); + B b = wrap_g(title, a); + try { + realAction(a, b); + } catch (...) { + showDialog(...) + } +} catch( PreconditionException ) { + // Only precondition checks get us here. + return; +} +``` + +or (since PreconditionException is not thrown from realAction) + +```java +try { + A a = wrap_f(title); + B b = wrap_g(title, a); + realAction(a, b); +} catch (...) { + // Only realAction gets us here + showDialog(...) +} catch( PreconditionException ) { + // Only precondition checks get us here. + return; +} +``` + +or (separate try-catch for preconditions and realAction) + +```java +A a = null; +B b = null; +try { + a = wrap_f(title); + b = wrap_g(title, a); +} catch( PreconditionException ) { + return; +} +try { + realAction(a, b); +} catch (...) { +} +``` + +or to reduce passing around the title part: + +#### [PreconditionException, dialog in catch] + +```java +A wrap_f() throws PreconditionException { + try { + return f(); + } catch (F ex) { + throw new PreconditionException(message, ex); + } +} + +B wrap_g(A a) throws PreconditionException { + try { + return g(a); + } catch (G ex) { + throw new PreconditionException(message, ex); + } +} +``` + +use + +```java +try { + A a = wrap_f(); + B b = wrap_g(a); + try { + realAction(a, b); + } catch (...) { + showDialog(...); + } +} catch(PreconditionException ex) { + showDialog(title, ex.message ); + return; +} +``` + +or + +```java +try { + A a = wrap_f(); + B b = wrap_g(a); + realAction(a, b); +} catch (...) { + showDialog(...); +} catch(PreconditionException ex) { + showDialog(title, ex.message ); + return; +} +``` + + +## Push associating the message further down + +As [the developers guide](https://jabref.readthedocs.io/en/latest/getting-into-the-code/code-howtos/#throwing-and-catching-exceptions) +suggest, we could "Catch and wrap all API exceptions" and rethrow them +as a `JabRefException` or some exception derived from it. In this case the try-catch part goes even further down, and +in principle we could just + +```java +try { + A a = f(); + B b = g(a); + realAction(a, b); +} catch(JabRefException ex) { + showDialog(title, ex.message ); + return; +} +``` + +Constraints: + +- conversion to `JabRefException` cannot be done in `model` (since JabRefException is in `logic`) +- `JabRefException` expects a localized message. Or we need to remember +which `JabRefException` instances are localized and which need to be caught +for localizing the message. +- At the bottom we usually have very little information on higher level +contexts: at a failure like `NoSuchProperty` we cannot tell which set +of properties did we look in and why. +For messages originating too deeply, we might want to override or extend the message anyway. +- for each exeption we might want to handle programmatically, we need a variant based +on `JabRefException` + +So we might end up: + +```java +try { + A a = f(); + B b = g(a); + realAction(a, b); +} catch(FDerivedFromJabRefException ex) { + showDialog(title, messageForF ); +} catch(GDerivedFromJabRefException ex) { + showDialog(title, messageForG ); +} catch(JabRefException ex) { + showDialog(title, ex.message ); +} catch(Exception ex) { // [1] + showDialog(title, ex.message, ex ); + // [1] does "never catch Exception or Throwable" apply at this point? + // Probably should not: we are promising not to throw. +} +``` + +which looks very similar to the original version. + +This again loses the information: can `GDerivedFromJabRefException` +come from `realAction` or `f` or not? This is because we have pushed +down the last catch/throw indefinitely (eliminating `wrap_f`) into a +depth, where we cannot necessarily assign an appropriate message. + +To a lesser extent this also happens in `wrap_f`: it only knows about +the action that called it what we provide (`title` or nothing). It knows +the precondition it checks: probably an optimal location to assign a message. + +**Summary**: going from top to bottom, we move to increasingly more local context, +our knowledge shifts towards the "in which part of the code did we have a problem" +and away from the high level ("which action"). + +One natural point to meet information from these to levels +is the top level of action handlers. For precondition checking code +a wrapper around code elsewhere may be considered. +Using such wrappers may reduce duplication if called in multiple actions. + +We still have to signal failure to the action handler: the options considered +above were using an `Optional` and throwing an exception with the appropriate message. + +The more promising variants were + +- **[dialog in wrap, return Optional]** + `Optional wrap_f(String title)` (showDialog inside) + - pro: explicit return in caller + - con: explicit return in caller (boilerplate) + - con: passing in the title is repeated + - would be 'pro' if we wanted title to vary within an action + +- **[PreconditionException, dialog in catch]** + `A wrap_f() throws PreconditionException` + (with `showDialog` under `catch(PreconditionException ex)`) + - con: hidden control flow + - pro: no repeated `if(){return}` boilerplate + - pro: title used only once + +### [using OOResult] + +```java +final String title = "Could not insert citation"; + +OOResult odoc = getXTextDocument(); +if (testDialog(title, + odoc, + styleIsRequired(style), + selectedBibEntryIsRequired(entries, OOError::noEntriesSelectedForCitation))) { + return; +} +XTextDocument doc = odoc.get(); + +OOResult ofr = getFrontend(doc); +if (testDialog(title, ofr)) { + return; +} +OOFrontend fr = ofr.get(); + +OOResult cursor = getUserCursorForTextInsertion(doc); +if (testDialog(title, cursor)) { + return; +} +... +``` + + +### [using PreconditionException, dialog in catch] + +```java +final String title = "Could not insert citation"; + +try { + XTextDocument doc = getXTextDocument(); + styleIsRequired(style); + selectedBibEntryIsRequired(entries, OOError::noEntriesSelectedForCitation); + OOFrontend fr = getFrontend(doc); + XTextCursor cursor = getUserCursorForTextInsertion(doc); + ... +} catch (PreconditionException ex) { + showDialog(title, ex); +} catch (...) { +} +``` + +I would suggest using the latter, + +- probably using `OOError` for `PreconditionException` + - In this case `OOError` being in `gui` becomes an asset: we can be sure + code in `logic` cannot throw it. +- We lose the capability to collect mmessages in a single dialog (we + stop processing at the first problem). +- The division between precondition checking (only throws + PreconditionException) and `realAction`becomes invisible in the + action code. + diff --git a/docs/openoffice/ooresult-ooerror.md b/docs/openoffice/ooresult-ooerror.md new file mode 100644 index 00000000000..867ee55d009 --- /dev/null +++ b/docs/openoffice/ooresult-ooerror.md @@ -0,0 +1,232 @@ +# About `OOError`, `OOResult` and `OOVoidResult` + +## Context + +### Relieve GUI panel code + +On the question of where should we catch exceptions in relation to GUI +code it was suggested (Jonatan Asketorp +[here](https://github.com/koppor/jabref/pull/496#discussion_r629695493), "most +of them (all?) should be handled latest in the ViewModel.") that +catching them early could help simplifying the higher levels. + +### Same messages in different contexts + +Some types of exceptions are caught in *different GUI actions*, often +resulting in basically the same error dialog, possibly only differing in +the indicated context (which GUI action). + +Problems found during *precondition checking* (for example: do we have +a connection to a document) and error conditions (for example: lost +connection to a document during an action) can overlap. + +### OOBibBase as a precondition and exception handling layer + +Since most of the code originally in `OOBibBase` was moved to `logic` and +almost all GUI actions go through `OOBibBase`, it seemed a good location +to collect precondition checking and exception handling code. + +Note: some of the precondition checking still needs to stay in `OpenOfficePanel`: +for example to provide a list of selected `BibEntry` instances, it needs to go through some steps +from `frame.getCurrentLibraryTab()` to `(!entries.isEmpty() && checkThatEntriesHaveKeys(entries))` + +To avoid `OOBibBase` depending on the higher level `OpenOfficePanel` +message texts needed in `OOBibBase` were moved from `OpenOfficePanel` to `OOError`. +(Others stayed, but could be moved if that seems worthwile) + +## OOError + +- `OOError` is a collection of data used in error dialogs. + - It is a `JabRefException` with an added field: `localizedTitle` + - It can store: a dialog title, a localized message (optionally a non-localized message as well) and a `Throwable` + - I used it in `OOBibBase` as a unified format for errors to be shown in an error dialog. + - Static constructors in `OOError` provide uniform translation from some exception types to + `OOError` with the corresponding localized messages: + `public static OOError from(SomeException ex)` + There is also `public static OOError fromMisc(Exception ex)` for exception types + not handled individually. (It has a different name, to avoid ambiguity) + + - Another set of contructors provide messages for some preconditions. + For example `public static OOError noDataBaseIsOpenForCiting()` + +Some questions: + +- Should we use static data instead of static methods for the precondition-related messages? + - pro: why create a new instance for each error? + - con: `OOError.setTitle()` currently just sets `this.localizedTitle` and returns `this`. + For static instances this would modify a shared resource unless we create a new copy in `setTitle`. + However `setTitle` can be called repeatedly on the same object: as we bubble up, we can be more specific + about the context. + +- Should we remove title from `OOError`? + - pro: we almost always override its original value + - con: may need to duplicate the title in different files (preconditions for an action in OpenOfficePanel + and in OOBibBase) + +- Should we include `OOError.showErrorDialog` ? + - pro: since it was intended *for* error dialogs, it is nice to provide this. + - con: the reference to `DialogService` forces it to `gui`, thus it cannot be used in `logic` or `model` + +- Should we use `JabRefException` as base? + - pro: `JabRefException` is mentioned as the standard form of errors in the developers guide. + [All Exceptions we throw should be or extend JabRefException](https://jabref.readthedocs.io/en/latest/getting-into-the-code/code-howtos/#throwing-and-catching-exceptions) + - against: `JabRefException` is in `logic` cannot be used in model. + (Could this be resolved by moving `JabRefException` to `model`?) + +## OOResult + +During precondition checking + +1. some tests return no data, only report problems +2. we may need to get some resources that might not be available + (for example: connection to a document, a functional textview cursor) +3. some test depend on these resources + +While concentrating on these and on "do not throw exceptions here" +... using a [Result type](https://en.wikipedia.org/wiki/Result_type) as a return +value from precondition checking code seemed a good fit: + +- Instead of throwing an exception, we can return some data describing the problem. +- Conceptually it is a data structure that either holds the result (of a computation) or and error value. +- It can be considered as an extended `Optional`, that can provide details on "why empty"? +- It can be considered as an alternative to throwing an exception: we return an `error` instead. +- Methods throwing checked exceptions cannot be used with for example `List.map`. + Methods returning a Result could. + +- `Result` shares the problem (with any other solutions) that in a +function several types of errors may occur, but we can only return a +single error type. Java solves this using checked exceptions being all +descendants of Exception. (Also adds try/catch/catch to select cases +based on the exceptions type, and some checking against forgotten +cases of checked exception types) + +In `OOBibBase` I used `OOError` as the unified error type: it can +store error messages and wrap exceptions. It contains everything we need +for an error dialog. On the other hand it does not support programmatic +dissection. + +### Implementation + +Unlike `Optional` and `List`, `Result` (in the sense used here) did not get into +java standard libraries. There are some implementations of this idea for java on the net: + +- [bgerstle/result-java](https://github.com/bgerstle/result-java/) +- [MrKloan/result-type](https://github.com/MrKloan/result-type) +- [david-bakin](https://gist.github.com/david-bakin/35d55daeeaee1eb71cea) +- [vavr-try](https://www.baeldung.com/vavr-try) + +Generics allow an implementation built around + +```java +class OOResult { + private final Optional result; + private final Optional error; +} +``` + +with an assumption that at any time exactly one of `result` and `error` is present. + +> `class X { boolean isOK; Object data; }` expresses this assumption more directly, +> (but omits the relation between the type parameters `` and the type in `data`) + +- Since `OOResult` encodes the state `isOK` in `result.isPresent()` (and equivalently in `errror.isEmpty()`), + we cannot allow construction of instances where both values are `isEmpty`. + In particular, `OOResult.ok(null)` and `OOResult.error(null)` are not + allowed: it would make the state `isOK` ambiguous. + It would also break the similarity to `Optional` to allow both `isEmpty` and `isOK` to be true. + +- Not allowing null, has a consequence on `OOResult` + According to + [baeldung.com/java-void-type](https://www.baeldung.com/java-void-type), + the only possible value for `Void` is `null` which we excluded. + + `OOResult.ok(null)` would look strange: in this case we need + `ok()` without arguments. + +To solve this problem, I introduced + +```java +class OOVoidResult { + private final Optional error; + ... +} +``` + +with methods on the error side similar to those in `OOError`, and +`OOVoidResult.ok()` to construct the success case with no data. + +### The relation between `Optional` and `OOVoidResult` + +- Both `Optional` and `OOVoidResult` can store 0 or 1 values, + in this respect they are equivalent + + - Actually, `OOVoidResult` is just a wrapper around an `Optional` + +- In terms of communication to human readers when used, their + connotation in respect to success and failure is the opposite: + + - `Optional.empty()` normally suggests failure, `OOVoidResult.ok()` mean success. + - `Optional.of(something)` probably means success, `OOVoidResult.error(something)` indicates failure. + - `OOVoidResult` is "the other half" (the failure branch) of `OOResult` + + - its content is accessed through `getError`, `mapError`, `ifError`, not `get`, `map`, `ifPresent` + +`OOVoidResult` allows + +- a clear distinction between success and failure when + calls to "get" something that might not be available (`Optional`) and + calls to precondition checking where we can only get reasons for failure + (`OOVoidResult`) + appear together. + Using `Optional` for both is possible, but is more error-prone. + +- it also allows using uniform verbs (`isError`, `getError`, + `ifError`, return `OO{Void}Result.error`) for "we have a problem" + when + + - checking preconditions (`OOVoidResult`) is mixed with + - "I need an X" orelse "we have a problem" (`OOResult`) + +- at a functions head: + + - `OOVoidResult function()` says: no result, but may get an error message + - `Optional function()` says: a `String` result or nothing. + +**Summary**: technically could use `Optional` for both situation, but it would be less precise, +leaving more room for confusion and bugs. `OOVoidResult` forces use of `getError` instead of `get`, +and `isError` or `isOk` instead of `isPresent`or `isEmpty`. + +## What does OOResult buy us? + + +The promise of `Result` is that we can avoid throwing exceptions and +return errors instead. This allows the caller to handle these latter +as data, for example may summarize / collect them for example into a single +message dialog. + +Handling the result needs some code in the caller. If we only needed checks that +return only errors (not results), the code could look like this (with possibly more tests listed): + +```java +OOResult odoc = getXTextDocument(); +if (testDialog(title, + odoc, + styleIsRequired(style), + selectedBibEntryIsRequired(entries, OOError::noEntriesSelectedForCitation))) { + return; +} +``` + +with a reasonably small footstep. + +Dependencies of tests on earlier results complicates this: now we repeat the + +```java +if (testDialog(title, + ...)) { + return; +} +``` + +part several times. + diff --git a/docs/openoffice/order-of-appearance.md b/docs/openoffice/order-of-appearance.md new file mode 100644 index 00000000000..e43009b9f93 --- /dev/null +++ b/docs/openoffice/order-of-appearance.md @@ -0,0 +1,118 @@ + +# Order of appearance of citation groups (`globalOrder`) + +The order of appearance of citations is decided on +two levels: + +1. their order within each citation group (`localOrder`), and +2. the order of the citation +groups that appear as citation markers in the text (`globalOrder`). + +This page is about the latter: how to decide the order of appearance (numbering sequence) of a set +of citation markers? + +## Conceptually + +In a continuous text it is easy: take the textual order of citation markers. + +In the presence of figures, tables, footnotes/endnotes possibly far from the location they are +referred to in the text or wrapped around with text it becomes less obvious what is the correct +order. + +Examples: + +- References in footnotes: are they *after* the page content, or number them as if they appeared at + the footnote mark? (JabRef does the latter) +- A figure with references in its caption. Text may flow on either or both sides. + Where should we insert these in the sequence? +- In a two-column layout, a text frame or figure mostly, but not fully in the second column: shall + we consider it part of the second column? + + +## Technically + + +In LibreOffice, a document has a main text that supports the +[XText](https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1text_1_1XText.html) +interface. +This allows several types of +[XTextContent](https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1text_1_1XTextContent.html) +to be inserted. + +- Some of these allow text inside with further insertions. + +### Anchors + +- Many, but not all XTextContent types support getting a "technical" insertion point or text range + through [getAnchor](https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1text_1_1XTextContent.html#ae82a8b42f6b2578549b68b4483a877d3). +- In Libreoffice positioning both a frame and its anchor seems hard: moving the frame tends to also + move the anchor. +- Consequence: producing an order of appearance for the citation groups based solely on `getAnchor` + calls may be impossible. + + - Allowing or requiring the user to insert "logical anchors" for frames and other "floating" parts + might help to alleviate these problems. + +### Sorting within a `Text` + +The text ranges occupied by the citation markers support the +[XTextRange](https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1text_1_1XTextRange.html) +interface. + +- These provide access to the XText they are contained in. +- The [Text](https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1text_1_1Text.html) service +may support (optional) the [XTextRangeCompare](https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1text_1_1XTextRangeCompare.html) +interface, that allows two XTextRange values to be compared if both belong to this `Text` + +### Visual ordering + +- The cursor used by the user is available as an + [XTextViewCursor](https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1text_1_1XTextViewCursor.html) +- If we can get it and can set its position in the document to each XTextRange to be sorted, and ask its + [getPosition](https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1text_1_1XTextViewCursor.html#a9b2bafd342ef75b5d504a9313dbb1389) + to provide coordinates "relative to the top left position of the first page of the document.", + then we can sort by these coordinates in top-to-bottom left-to-right order. +- Note: in some cases, for example when the cursor is in a comment (as in + `Libreoffice:[menu:Insert]/[Comment]`), the XTextViewCursor is not available (I know of no way to + get it). +- In some other cases, for example when an image is selected, the XTextViewCursor we normally receive is not 'functional': +we cannot position it for getting coordinates for the citation marks. +The [FunctionalTextViewCursor](https://github.com/antalk2/jabref/blob/improve-reversibility-rebased-03/src/main/java/org/jabref/model/openoffice/rangesort/FunctionalTextViewCursor.java) +class can solve this case by accessing and manipulating the cursor through [XSelectionSupplier](https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1view_1_1XSelectionSupplier.html) + +Consequences of getting these visual coordinates and using them to order the citation markers + +- allows uniform handling of the markers. Works in footnotes, tables, frames (apparently anywhere) +- requires moving the user visible cursor to each position and with [screen + refresh](https://github.com/antalk2/jabref/blob/improve-reversibility-rebased-03/src/main/java/org/jabref/model/openoffice/uno/UnoScreenRefresh.java) + enabled. + `(problem)` This results in some user-visible flashing and scrolling around in the document view. +- The expression "relative to the top left position of the first page of the document" is + understood literally, "as on the screen". + `(problem)` Showing pages side by side or using a two-column layout + will result in markers in the top half of the second column or page to be sorted before those on the bottom + of the first column of the first page. + + +## JabRef + +Jabref uses the following steps for sorting sorting citation markers (providing `globalOrder`): + +1. the textranges of citation marks in footnotes are replaced by the textranges of the footnote + marks. +2. get the positions (coordinates) of these marks +3. sort in top-to-botton left-to-right order + + +`(problem)` In JabRef5.2 the positions of citation marks within the same footnote become +indistinguishable, thus their order after sorting may differ from their order in the footnote text. +This caused problems for + +1. numbering order + `(solved)` by keeping track of the order-in-footnote of citation markers during sorting using + [getIndexInPosition](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortable.java#L21)) +2. `click:Merge`: It examines *consecutive* pairs of citation groups if they can be merged. Wrong +order may result in not discovering some mergeable pairs or attempting to merge in wrong order. +`(solved)` by not using visual order, only XTextRangeCompare-based order within each XText +[here](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java#L325)) + diff --git a/docs/openoffice/overview.md b/docs/openoffice/overview.md new file mode 100644 index 00000000000..f3a6ed251f6 --- /dev/null +++ b/docs/openoffice/overview.md @@ -0,0 +1,297 @@ + +# Overview + +This is a partial overview of the OpenOffice/LibreOffice panel and the +code behind. + +- To access the panel: `JabRef:/[menu:View]/[OpenOffice/LibreOffice]` +- The user documentation is at +[https://docs.jabref.org/cite/openofficeintegration](https://docs.jabref.org/cite/openofficeintegration) + + +I am going to refer to OpenOffice Writer and LibreOffice Writer as +LibreOffice or LO: their UNO APIs are still mostly identical, but I +only tested with LibreOffice and differences do exist. + +## Subject + +- What is stored in a document, how. +- Generating citation markers and bibliography + - (excluding the bibliography entries, which is delegated to the layout module) + + +## The purpose of the panel + +- Allow the user to insert **citations** in a LibreOffice writer document. +- Automatically format these according to some prescribed style as **citation markers**. +- Generate a **bibliography**, also formatted according to the style. + - The bibliography consists of a title (e.g. "References") and a sorted list + of formatted bibliography entries, possibly prefixed with a marker (e.g. "[1]") +- It also allows some related activities: connect to a document, select a style, group ("Merge") the + citations for nicer output, ungroup ("Separate") them to move or delete them individually, + edit ("Manage") their page-info parts, and collect the database entries of cited sources + to a new database. + +## Citation types + +Citations (actually citation groups, see below) have three types +depending on how the citation marker is intended to appear in the +text: + +- **Parenthesized**: "(Smith, 2000)" +- **In-text**: "Smith (2000)" +- **Invisible**: no visible citation mark. + - An invisible citation mark lets the user to use any form for the citation + by taking control (and responsibility) back from the style. + - Like the other two citation types, they have a location in the document. + - In the bibliography these behave as the other two citation types. + - In LibreOffice (`LibreOffice:[Ctrl-F8]` or`LibreOffice:[menu:View]/[Field Shadings]`) + shows reference marks with gray background. Invisible citation marks appear as a thin gray rectangle. + +- These citation types correspond to `\citep{Smith2000}`, + `\citet{Smith2000}` in + [natbib](http://tug.ctan.org/macros/latex/contrib/natbib/natnotes.pdf) + and `\nocite{Smith2000}`. I will use `\citep`, `\citet` and `\citen` in "LaTeX pseudocode" below. + +## PageInfo + +The citations can be augmented with a string detailing which part +of a document is cited, for example "page 11" or "chapter 2". + +Sample citation markers (with LaTeX pseudocode): + +- `\citep[page 11]{Smith2000}` "(Smith, 2000; page 11)" +- `\citet[page 11]{Smith2000}` "Smith (2000; page 11)" +- `\citen[page 11]{Smith2000}` "" + +- This string is referred to as **`pageInfo`** in the code. +- In the GUI the labels "Cite special", "Extra information + (e.g. page number)" are used. + +## Citation groups + +Citations can be grouped. + +A group of parenthesized citations share the parentheses around, like this: + "(Smith, 2000; Jones 2001)". + +- Examples with pseudocode: + - `\citep{Smith2000,Jones2001}` "(Smith, 2000; Jones 2001)" + - `\citet{Smith2000,Jones2001}` "Smith (2000); Jones (2001)" + - `\citen{Smith2000,Jones2001}` "" + +From the user's point of view, citation groups can be created by + +1. Selecting multiple entries in a bibliography database, then + - `[click:Cite]` or + - `[click:Cite in-text]` or + - `[click:Cite special]` or + - `[click:Insert empty citation]` in the panel. + + This method allows any of the citation types to be used. + +2. `[click:Merge citations]` finds all sets of consecutive citations in the text and + replaces each with a group. + - `(change)` The new code only merges consecutive [parenthesized](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java#L183) citations. + - This is inconsistent with the solution used in `[click:Cite]` + - My impression is that + - groups of in-text or invisible citations are probably not useful + - mixed groups are even less. However, with a numbered style + there is no visual difference between parenthesized and in-text + citations, the user may be left wondering why did merge not work. + - One way out could be to merge as a "parenthesized" + group. But then users switching between styles get a + surprise, we have unexpectedly overridden their choice. + - I would prefer a visible log-like warning that does not require + a click to close and lets me see multiple warnings. + Could the main window have such an area at the bottom? + - Starting with JabRef 5.3 there is also `[click:Separate citations]` + that breaks all groups to single citations. + - This allows + - deleting individual citations + - moving individual citations around (between citation groups) + - (copy does not work) + - (Moving a citation within a group has no effect on the final output + due to sorting of citations within groups. See [Sorting within a citation group](#localOrder)) + +In order to manage single citations and groups uniformly, we +consider each citation in the document to belong to a citation +group, even if it means a group containing a single citation. + +Citation groups correspond to citation markers in the document. The latter is empty for invisible +citation groups. When creating the citation markers, the citations in the group +are processed together. + +# Citation styles + +The details of how to format the bibliography and the citation markers are described in a text file. + +- These normally use `.jstyle` extension, and I will refer to them as jstyle files. +- See the [User documentation](https://docs.jabref.org/cite/openofficeintegration#the-style-file) + for details. +- I will refer to keywords in jstyle files as `jstyle:keyword` below. + + +Four major types citation of styles can be described by a jstyle. + +- (1) `jstyle:BibTeXKeyCitations` + + - The citation markers show the citationKey. + - It is not fully implemented + - does not produce markers before the bibliography entries + - does not show pageInfo + - It is not advertised in the [User documentation](https://docs.jabref.org/cite/openofficeintegration#the-style-file). + - Its intended purpose may be + + - (likely) a proper style, with "[Smith2000]" style citation markers + - (possibly) a style for "draft mode" that + - can avoid lookup of citation markers in the database when only the + citation markers are updated + - can produce unique citation markers trivially (only needs local information) + - makes the citation keys visible to the user + - can work without knowing the order of appearance of citation groups + - In case we expect to handle larger documents, a "draft mode" + minimizing work during `[click:Cite]` may be useful. + +- There are two types of numbered (`jstyle:IsNumberEntries`) citation styles: + + - (2) Citations numbered in order of first appearance (`jstyle:IsSortByPosition`) + - (3) Citations numbered according to their order in the sorted bibliography + +- (4) Author-year styles + +# Sorting + +## Sorting te bibliography + +The bibliography is sorted in (author, year, title) order + +- except for `jstyle:IsSortByPosition`, that uses the order of first + appearance of the cited sources. + +## Ordering the citations + +The order of appearance of citations (as considered during numbering and adding letters after the +year to ensure that citation markers uniquely identify sources in the bibliography) is decided on +two levels. + +1. Their order within each citation group (`localOrder`), and +2. the order of the citation groups (citation markers) in the text (`globalOrder`). + +### Sorting within a citation group (`localOrder`) + +The order of citations within a citation group is controlled by +`jstyle:MultiCiteChronological`. + +- true asks for (year, author, title) ordering, +- false for (author, year, title). +- (There is no option for "in the order provided by the user"). + + +For author-year citation styles this ordering is used directly. + +- The (author, year, title) order promotes discovering citations + sharing authors and year and emitting them in a shorter form. For + example as "(Smith 2000a,b)". + +For numbered styles, the citations within a group are sorted again +during generation of the citation marker, now by the numbers +themselves. The result of this sorting is not saved, only affects the citation marker. + +- Series of consecutive number are replaced with ranges: for example "[1-5; 11]" + +### Order of the citation groups (`globalOrder`) + +The location of each citation group in the document is provided by the +user. In a text with no insets, footnotes, figures etc. this directly +provides the order. In the presence of these, it becomes more +complicated, see [Order of appearance of +citation groups](order-of-appearance.md). + +### Order of the citations + +- `globalOrder` and `localOrder` together provide the order of appearance of + citations +- This also provides the order of first appearance of the cited sources. + + First appearance order of sources is used + - in `jstyle:IsSortByPosition` numbered styles + - in author-year styles: first appearance of "Smith200a" + should precede that of "Smith200b". + To achieve this, the sources get the letters + according the order of their first appearance. + - This seems to contradict the statement "The bibliography is + sorted in (author, year, title) order" above. + It does not. As of JabRef 5.3 both are true. + Consequence: in the references + Smith2000b may precede Smith2000a. + ([reported](https://github.com/JabRef/jabref/issues/7805)) + - Some author-year citation styles prescribe a higher threshold on + the number of authors for switching to "FirstAuthor et al." form + (`jstyle:MaxAuthors`) at the first citation of a source + (`jstyle:MaxAuthorsFirst`) + + +# What is stored in a document (JabRef5.2) + +- Each group of citations has a reference mark. + + (Reference marks are shown in LibreOffice in Navigator, under "References". + To show the Navigator: `LibreOffice:[menu:View]/[Navigator]` or `LibreOffice:[key:F5]`) + + Its purposes: + + 1. The text range of the reference mark tells where to write or update the citation mark. + 2. The name of the reference mark + + - Lets us select only those reference marks that belong to us + - Encodes the citation type + - Contains the list of citation keys that belong to this group + - It may contain an extra number, to make the name unique in the document + - Format: `"JR_cite{number}_{type}_{citationKeys}"`, where + - `{number}` is either empty or an unsigned integer (it can be zero) to make the name unique + - `{type}` is 1, 2, or 3 for parenthesized, in-text and invisible + - `{citationKeys}` contains the comma-separated list of citation keys + - Examples: + - `JR_cite_1_Smith2000` (empty number part, parenthesized, single citation) + - `JR_cite0_2_Smith2000,Jones2001` (number part is 0, in-text, two citations) + - `JR_cite1_3_Smith2000,Jones2001` (number part is 1, invisible, two citations) + +- Each group of citations may have an associated pageInfo. + + - In LibreOffice, these can be found at + `LibreOffice:/[menu:File]/[Properties]/[Custom Properties]` + - The property names are identical to the name of the reference mark + corresponding to the citation group. + - JabRef 5.2 never cleans up these, they are left around. + `(problem)` New citations may "pick up" these unexpectedly. + +- The bibliography, if not found, is created at the end of the document. + - The location and extent of the bibliography is the content of the Section named `"JR_bib"`. + (In LibreOffice Sections are listed in the Navigator panel, under "Sections") + - JabRef 5.2 also creates a bookmark named `"JR_bib_end"`, but does + not use it. During bibliography update it attempts to create it again without + removing the old bookmark. The result is a new bookmark, with a number appended to its name + (by LibreOffice, to ensure unique names of bookmarks). + - [Correction in new code](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/logic/openoffice/frontend/UpdateBibliography.java#L147): + remove the old before creating the new. + +# How does it interact with the document? + +- "stateless" + JabRef is only loosely coupled to the document. + Between two GUI actions it does not receive any information from LibreOffice. + It cannot distinguish between the user changing a single character in the document or rewriting everything. + +- Access data + - During a `[click:cite]` or `[click:Update]` we need the reference mark names. + - Get all reference mark names + - Filter (only ours) + - Parse: gives citation type (for the group), citation keys + - Access/store pageInfo: based on reference mark name and property name being equal + - Creating a citation group: (`[click:cite]`) + - Creates a reference mark at the cursor, with a name as described above. +- Update (refreshing citation markers and bibliography): + - citation markers: the content of the reference mark + - bibliography: the content of the Section (in LibreOffice sense) + named `"JR_bib"`. diff --git a/docs/openoffice/problems.md b/docs/openoffice/problems.md new file mode 100644 index 00000000000..c23b39a7d25 --- /dev/null +++ b/docs/openoffice/problems.md @@ -0,0 +1,97 @@ +# Problems in JabRef 5.2 + +## pageInfo should belong to citations, not citation groups + +- Creating `[click:Separate]` revealed + a `(problem)`: pageInfo strings are conceptually associated with + citations, but the implementation associates them to citation groups. + The number of available + pageInfo slots changes during`[click:Merge]` and `[click:Separate]` while the number of citations + remains fixed. + - The proposed solution was to change the association. + - Not only reference marks (citation groups) need unique identifiers, but also citations. + Possible encoding for reference mark names: + `JR_cite{type}_{number1}_{citationKey1},{number2}_{citationKey2}` + where `{type}` encodes the citation type (for the group), `{citationKey1}` is made unique by choosing an appropriate number for `{number1}` + This would allow + `JR_cite_{number1}_{citationKey1}` to be used as a property name for storing the pageInfo. + + Changes required to + - reference mark search, name generation and parsing + - name generation and parsing for properties storing pageInfo values + - in-memory representation + - JabRef 5.2 does not collect pageInfo values, accesses only when needed. + So it would be change to code accessing them. + - The proposed representation does collect, to allow separation of getting from the document + and processing + - insertion of pageInfo into citation markers: JabRef 5.2 injects a single pageInfo before the closing parenthesis, + now we need to handle several values + - `[click:Manage citations]` should work on citations, not citation groups. + + +## Backend + +The choice of how do we represent the data and the citation marks in the document has consequences +on usability. + +Reference marks have some features that make it easy to mess up citations in a document + +- They are **not visible** by default, the user is not aware of their boundaries +(`LO:[key:Ctrl-F8]`, `LO:[View]/[Field shadings]` helps) + +- They are **not atomic**: + - the user can edit the content. This will be lost on `[click:Update]` + If an `As character` or `To character` anchor is inserted, the corresponding frame or footnote is deleted. + - by pressing Enter within, the user can break a reference mark into two parts. + The second part is now outside the reference mark: `[click:Update]` will leave it as is, and replace the first part + with the full text for the citation mark. + - If the space separating to citation marks is deleted, the user cannot reliably type between the + marks. + The text typed usually becomes part of one of the marks. No visual clue as to which one. + Note: `[click:Merge]` then `[click:Separate]` adds a single space between. The user can + position the cursor before or after it. In either case the cursor is on a boundary: it is not + clear if it is in or out of a reference mark. + Special case: a reference mark at the start or end of a paragraph: the cursor is usually considered to be within at the coresponding edge. +- (good) They can be moved (Ctrl-X,Ctrl-V) +- They cannot be copied. (Ctrl-C, Ctrl-V) copies the text without the reference mark. +- Reference marks are lost if the document is saved as docx. + +- I know of no way to insert text into an empty text range denoted by a reference mark + - JabRef 5.3 recreates the reference mark (using [insertReferenceMark](https://github.com/JabRef/jabref/blob/475b2989ffa8ec61c3327c62ed8f694149f83220/src/main/java/org/jabref/gui/openoffice/OOBibBase.java#L1072)) + [here](https://github.com/JabRef/jabref/blob/475b2989ffa8ec61c3327c62ed8f694149f83220/src/main/java/org/jabref/gui/openoffice/OOBibBase.java#L706) + - `(change)` I preferred to (try to) avoid this: + [NamedRangeReferenceMark.nrGetFillCursor](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java#L225) + returns a cursor between two invisible + spaces, to provide the caller a location it can safely write some text. [NamedRangeReferenceMark.nrCleanFillCursor](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java#L432) + removes these invisible spaces unless the content would become empty or a single character. By + keeping the content at least two characters, we avoid the ambiguity at the edges: a cursor + positioned between two characters inside is always within the reference mark. (At the edges it + may or may not be inside.) + +- `(change)` `[click:Cite]` at reference mark edges: [safeInsertSpacesBetweenReferenceMarks](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java#L67) ensures the we are not inside, by starting two new paragraphs, inserting two spaces between them, then removing the new paragraph marks. +- `(change)` [guiActionInsertEntry](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/gui/openoffice/OOBibBase2.java#L624) +checks if the cursor is in a citation mark or the bibliography. + +- `(change)` `[click:Update]` does an [exhaustive check](https://github.com/antalk2/jabref/blob/122d5133fa6c7b44245c5ba5600d398775718664/src/main/java/org/jabref/gui/openoffice/OOBibBase2.java#L927) +for overlaps between protected ranges (citation marks and bibliography). This can become slow if there are many citations. + + +It would be nice if we could have a backend with better properties. We probably need multiple +backends for different purposes. This would be made easier if the backend were separated from the +rest of the code. This would be the purpose of +[logic/openoffice/backend](https://github.com/antalk2/jabref/tree/improve-reversibility-rebased-03/src/main/java/org/jabref/logic/openoffice/backend). + +## Undo + +- JabRef 5.3 does not collect the effects of GUI actions on the document into larger Undo actions. +This makes the Undo functionality of LO impractial. +- `(change)` collect the effects of GUI actions into large chunks: now a GUI action can be undone +with a single click. + - except the effect on pageInfo: that is stored at the document level and is not restored by Undo. + +## Block screen refresh + +- LibreOffice has support in [XModel](https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1frame_1_1XModel.html#a7b7d36374033ee9210ec0ac5c1a90d9f) +to "suspend some notifications to the controllers which are used for display updates." + +- `(change)` Now we are using this facility. diff --git a/src/main/java/org/jabref/gui/openoffice/AdvancedCiteDialogView.java b/src/main/java/org/jabref/gui/openoffice/AdvancedCiteDialogView.java index f11e8ab90ae..e30eec67c3d 100644 --- a/src/main/java/org/jabref/gui/openoffice/AdvancedCiteDialogView.java +++ b/src/main/java/org/jabref/gui/openoffice/AdvancedCiteDialogView.java @@ -24,6 +24,7 @@ public AdvancedCiteDialogView() { ViewLoader.view(this) .load() .setAsDialogPane(this); + setResultConverter(btn -> { if (btn == ButtonType.OK) { return viewModel; diff --git a/src/main/java/org/jabref/gui/openoffice/BibEntryNotFoundException.java b/src/main/java/org/jabref/gui/openoffice/BibEntryNotFoundException.java deleted file mode 100644 index b3db5f8f9e7..00000000000 --- a/src/main/java/org/jabref/gui/openoffice/BibEntryNotFoundException.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.jabref.gui.openoffice; - -class BibEntryNotFoundException extends Exception { - - private final String citationKey; - - public BibEntryNotFoundException(String citationKey, String message) { - super(message); - - this.citationKey = citationKey; - } - - public String getCitationKey() { - return citationKey; - } -} diff --git a/src/main/java/org/jabref/gui/openoffice/ConnectionLostException.java b/src/main/java/org/jabref/gui/openoffice/ConnectionLostException.java deleted file mode 100644 index df0264be0de..00000000000 --- a/src/main/java/org/jabref/gui/openoffice/ConnectionLostException.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.jabref.gui.openoffice; - -/** - * This exception is used to indicate that connection to OpenOffice has been lost. - */ -class ConnectionLostException extends RuntimeException { - - public ConnectionLostException(String msg) { - super(msg); - } -} diff --git a/src/main/java/org/jabref/gui/openoffice/DetectOpenOfficeInstallation.java b/src/main/java/org/jabref/gui/openoffice/DetectOpenOfficeInstallation.java index 8141d5c7141..719cdd4b870 100644 --- a/src/main/java/org/jabref/gui/openoffice/DetectOpenOfficeInstallation.java +++ b/src/main/java/org/jabref/gui/openoffice/DetectOpenOfficeInstallation.java @@ -21,18 +21,18 @@ */ public class DetectOpenOfficeInstallation { - private final OpenOfficePreferences ooPrefs; + private final OpenOfficePreferences openOfficePreferences; private final DialogService dialogService; private final PreferencesService preferencesService; public DetectOpenOfficeInstallation(PreferencesService preferencesService, DialogService dialogService) { this.preferencesService = preferencesService; this.dialogService = dialogService; - this.ooPrefs = preferencesService.getOpenOfficePreferences(); + this.openOfficePreferences = preferencesService.getOpenOfficePreferences(); } public boolean isExecutablePathDefined() { - return checkAutoDetectedPaths(ooPrefs); + return checkAutoDetectedPaths(openOfficePreferences); } public Optional selectInstallationPath() { @@ -71,8 +71,8 @@ public boolean setOpenOfficePreferences(Path installDir) { } if (execPath.isPresent()) { - ooPrefs.setExecutablePath(execPath.get().toString()); - preferencesService.setOpenOfficePreferences(ooPrefs); + openOfficePreferences.setExecutablePath(execPath.get().toString()); + preferencesService.setOpenOfficePreferences(openOfficePreferences); return true; } @@ -88,12 +88,11 @@ public Optional chooseAmongInstallations(List installDirs) { return Optional.of(installDirs.get(0).toAbsolutePath()); } - String content = Localization.lang("Found more than one OpenOffice/LibreOffice executable.") - + "\n" + Localization.lang("Please choose which one to connect to:"); - - Optional selectedPath = dialogService.showChoiceDialogAndWait(Localization.lang("Choose OpenOffice/LibreOffice executable"), - content, Localization.lang("Use selected instance"), installDirs); - - return selectedPath; + return dialogService.showChoiceDialogAndWait( + Localization.lang("Choose OpenOffice/LibreOffice executable"), + Localization.lang("Found more than one OpenOffice/LibreOffice executable.") + "\n" + + Localization.lang("Please choose which one to connect to:"), + Localization.lang("Use selected instance"), + installDirs); } } diff --git a/src/main/java/org/jabref/gui/openoffice/ManageCitationsDialogView.java b/src/main/java/org/jabref/gui/openoffice/ManageCitationsDialogView.java index a312a4ef375..d954dc55667 100644 --- a/src/main/java/org/jabref/gui/openoffice/ManageCitationsDialogView.java +++ b/src/main/java/org/jabref/gui/openoffice/ManageCitationsDialogView.java @@ -19,9 +19,6 @@ import org.jabref.model.strings.StringUtil; import com.airhacks.afterburner.views.ViewLoader; -import com.sun.star.beans.UnknownPropertyException; -import com.sun.star.container.NoSuchElementException; -import com.sun.star.lang.WrappedTargetException; public class ManageCitationsDialogView extends BaseDialog { @@ -56,7 +53,7 @@ public ManageCitationsDialogView(OOBibBase ooBase) { } @FXML - private void initialize() throws NoSuchElementException, WrappedTargetException, UnknownPropertyException { + private void initialize() { viewModel = new ManageCitationsDialogViewModel(ooBase, dialogService); @@ -70,9 +67,8 @@ private void initialize() throws NoSuchElementException, WrappedTargetException, citationsTableView.itemsProperty().bindBidirectional(viewModel.citationsProperty()); - extraInfo.setOnEditCommit((CellEditEvent cell) -> { - cell.getRowValue().setExtraInfo(cell.getNewValue()); - }); + extraInfo.setOnEditCommit((CellEditEvent cell) -> + cell.getRowValue().setExtraInfo(cell.getNewValue())); extraInfo.setCellFactory(TextFieldTableCell.forTableColumn()); } @@ -87,7 +83,13 @@ private Node getText(String citationContext) { inBetweenText.setStyle("-fx-font-weight: bold"); Text endText = new Text(end); - FlowPane flow = new FlowPane(startText, inBetweenText, endText); - return flow; + return new FlowPane(startText, inBetweenText, endText); + } + + public boolean isOkToShowThisDialog() { + if (viewModel == null || viewModel.failedToGetCitationEntries) { + return false; + } + return true; } } diff --git a/src/main/java/org/jabref/gui/openoffice/ManageCitationsDialogViewModel.java b/src/main/java/org/jabref/gui/openoffice/ManageCitationsDialogViewModel.java index 40cee65036f..697212fde54 100644 --- a/src/main/java/org/jabref/gui/openoffice/ManageCitationsDialogViewModel.java +++ b/src/main/java/org/jabref/gui/openoffice/ManageCitationsDialogViewModel.java @@ -9,59 +9,34 @@ import javafx.collections.FXCollections; import org.jabref.gui.DialogService; -import org.jabref.logic.l10n.Localization; import org.jabref.model.openoffice.CitationEntry; -import com.sun.star.beans.IllegalTypeException; -import com.sun.star.beans.NotRemoveableException; -import com.sun.star.beans.PropertyExistException; -import com.sun.star.beans.UnknownPropertyException; -import com.sun.star.container.NoSuchElementException; -import com.sun.star.container.XNameAccess; -import com.sun.star.lang.IllegalArgumentException; -import com.sun.star.lang.WrappedTargetException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class ManageCitationsDialogViewModel { - private static final Logger LOGGER = LoggerFactory.getLogger(ManageCitationsDialogViewModel.class); - + public final boolean failedToGetCitationEntries; private final ListProperty citations = new SimpleListProperty<>(FXCollections.observableArrayList()); private final OOBibBase ooBase; private final DialogService dialogService; - public ManageCitationsDialogViewModel(OOBibBase ooBase, DialogService dialogService) throws NoSuchElementException, WrappedTargetException, UnknownPropertyException { + public ManageCitationsDialogViewModel(OOBibBase ooBase, DialogService dialogService) { this.ooBase = ooBase; this.dialogService = dialogService; - XNameAccess nameAccess = ooBase.getReferenceMarks(); - List names = ooBase.getJabRefReferenceMarks(nameAccess); - for (String name : names) { - - CitationEntry entry = new CitationEntry(name, - ooBase.getCitationContext(nameAccess, name, 30, 30, true), - ooBase.getCustomProperty(name)); + Optional> citationEntries = ooBase.guiActionGetCitationEntries(); + this.failedToGetCitationEntries = citationEntries.isEmpty(); + if (citationEntries.isEmpty()) { + return; + } + for (CitationEntry entry : citationEntries.get()) { CitationEntryViewModel itemViewModelEntry = new CitationEntryViewModel(entry); citations.add(itemViewModelEntry); } } public void storeSettings() { - List ciationEntries = citations.stream().map(CitationEntryViewModel::toCitationEntry).collect(Collectors.toList()); - try { - for (CitationEntry entry : ciationEntries) { - Optional pageInfo = entry.getPageInfo(); - if (pageInfo.isPresent()) { - ooBase.setCustomProperty(entry.getRefMarkName(), pageInfo.get()); - } - } - } catch (UnknownPropertyException | NotRemoveableException | PropertyExistException | IllegalTypeException | - IllegalArgumentException ex) { - LOGGER.warn("Problem modifying citation", ex); - dialogService.showErrorDialogAndWait(Localization.lang("Problem modifying citation"), ex); - } + List citationEntries = citations.stream().map(CitationEntryViewModel::toCitationEntry).collect(Collectors.toList()); + ooBase.guiActionApplyCitationEntries(citationEntries); } public ListProperty citationsProperty() { diff --git a/src/main/java/org/jabref/gui/openoffice/OOBibBase.java b/src/main/java/org/jabref/gui/openoffice/OOBibBase.java index 540c5f9216b..5ad5e2634c8 100644 --- a/src/main/java/org/jabref/gui/openoffice/OOBibBase.java +++ b/src/main/java/org/jabref/gui/openoffice/OOBibBase.java @@ -1,1424 +1,862 @@ package org.jabref.gui.openoffice; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.function.Supplier; import java.util.stream.Collectors; -import org.jabref.architecture.AllowedToUseAwt; import org.jabref.gui.DialogService; -import org.jabref.logic.bibtex.comparator.FieldComparator; -import org.jabref.logic.bibtex.comparator.FieldComparatorStack; +import org.jabref.logic.JabRefException; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.layout.Layout; -import org.jabref.logic.openoffice.OOUtil; -import org.jabref.logic.openoffice.UndefinedBibtexEntry; -import org.jabref.logic.openoffice.UndefinedParagraphFormatException; +import org.jabref.logic.openoffice.NoDocumentFoundException; +import org.jabref.logic.openoffice.action.EditInsert; +import org.jabref.logic.openoffice.action.EditMerge; +import org.jabref.logic.openoffice.action.EditSeparate; +import org.jabref.logic.openoffice.action.ExportCited; +import org.jabref.logic.openoffice.action.ManageCitations; +import org.jabref.logic.openoffice.action.Update; +import org.jabref.logic.openoffice.frontend.OOFrontend; +import org.jabref.logic.openoffice.frontend.RangeForOverlapCheck; import org.jabref.logic.openoffice.style.OOBibStyle; -import org.jabref.logic.openoffice.style.OOPreFormatter; import org.jabref.model.database.BibDatabase; import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.field.StandardField; +import org.jabref.model.openoffice.CitationEntry; +import org.jabref.model.openoffice.rangesort.FunctionalTextViewCursor; +import org.jabref.model.openoffice.style.CitationGroupId; +import org.jabref.model.openoffice.style.CitationType; import org.jabref.model.openoffice.uno.CreationException; import org.jabref.model.openoffice.uno.NoDocumentException; +import org.jabref.model.openoffice.uno.UnoCrossRef; +import org.jabref.model.openoffice.uno.UnoCursor; +import org.jabref.model.openoffice.uno.UnoRedlines; +import org.jabref.model.openoffice.uno.UnoStyle; +import org.jabref.model.openoffice.uno.UnoUndo; +import org.jabref.model.openoffice.util.OOResult; +import org.jabref.model.openoffice.util.OOVoidResult; -import com.sun.star.awt.Point; import com.sun.star.beans.IllegalTypeException; import com.sun.star.beans.NotRemoveableException; -import com.sun.star.beans.PropertyExistException; import com.sun.star.beans.PropertyVetoException; -import com.sun.star.beans.UnknownPropertyException; -import com.sun.star.beans.XPropertyContainer; -import com.sun.star.beans.XPropertySet; import com.sun.star.comp.helper.BootstrapException; import com.sun.star.container.NoSuchElementException; -import com.sun.star.container.XEnumeration; -import com.sun.star.container.XEnumerationAccess; -import com.sun.star.container.XNameAccess; -import com.sun.star.container.XNamed; -import com.sun.star.document.XDocumentPropertiesSupplier; -import com.sun.star.frame.XComponentLoader; -import com.sun.star.frame.XController; -import com.sun.star.frame.XDesktop; -import com.sun.star.frame.XModel; import com.sun.star.lang.DisposedException; -import com.sun.star.lang.IllegalArgumentException; -import com.sun.star.lang.Locale; import com.sun.star.lang.WrappedTargetException; -import com.sun.star.lang.XComponent; -import com.sun.star.lang.XMultiComponentFactory; -import com.sun.star.lang.XMultiServiceFactory; -import com.sun.star.text.XBookmarksSupplier; -import com.sun.star.text.XDocumentIndexesSupplier; -import com.sun.star.text.XFootnote; -import com.sun.star.text.XReferenceMarksSupplier; -import com.sun.star.text.XText; -import com.sun.star.text.XTextContent; import com.sun.star.text.XTextCursor; import com.sun.star.text.XTextDocument; -import com.sun.star.text.XTextRange; -import com.sun.star.text.XTextRangeCompare; -import com.sun.star.text.XTextSection; -import com.sun.star.text.XTextSectionsSupplier; -import com.sun.star.text.XTextViewCursor; -import com.sun.star.text.XTextViewCursorSupplier; -import com.sun.star.uno.Any; -import com.sun.star.uno.Type; -import com.sun.star.uno.UnoRuntime; -import com.sun.star.uno.XComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Class for manipulating the Bibliography of the currently start document in OpenOffice. + * Class for manipulating the Bibliography of the currently started document in OpenOffice. */ -@AllowedToUseAwt("Requires AWT for italics and bold") class OOBibBase { - private static final OOPreFormatter POSTFORMATTER = new OOPreFormatter(); + private static final Logger LOGGER = LoggerFactory.getLogger(OOBibBase.class); - private static final String BIB_SECTION_NAME = "JR_bib"; - private static final String BIB_SECTION_END_NAME = "JR_bib_end"; - private static final String BIB_CITATION = "JR_cite"; - private static final Pattern CITE_PATTERN = Pattern.compile(OOBibBase.BIB_CITATION + "\\d*_(\\d*)_(.*)"); + private final DialogService dialogService; - private static final String CHAR_STYLE_NAME = "CharStyleName"; + // After inserting a citation, if ooPrefs.getSyncWhenCiting() returns true, shall we also update the bibliography? + private final boolean refreshBibliographyDuringSyncWhenCiting; - private static final int AUTHORYEAR_PAR = 1; - private static final int AUTHORYEAR_INTEXT = 2; - private static final int INVISIBLE_CIT = 3; + // Shall we add "Cited on pages: ..." to resolved bibliography entries? + private final boolean alwaysAddCitedOnPages; - private static final Logger LOGGER = LoggerFactory.getLogger(OOBibBase.class); - private XMultiServiceFactory mxDocFactory; - private XTextDocument mxDoc; - private XText text; - private final XDesktop xDesktop; - private XTextViewCursorSupplier xViewCursorSupplier; - private XComponent xCurrentComponent; - private XPropertySet propertySet; - - private XPropertyContainer userProperties; - private final boolean atEnd; - private final Comparator entryComparator; - private final Comparator yearAuthorTitleComparator; - private final FieldComparator authComp = new FieldComparator(StandardField.AUTHOR); - private final FieldComparator yearComp = new FieldComparator(StandardField.YEAR); - - private final FieldComparator titleComp = new FieldComparator(StandardField.TITLE); - private final List> authorYearTitleList = new ArrayList<>(3); - - private final List> yearAuthorTitleList = new ArrayList<>(3); - private final Map uniquefiers = new HashMap<>(); - private List sortedReferenceMarks; - - private final DialogService dialogService; + private final OOBibBaseConnect connection; - public OOBibBase(Path loPath, boolean atEnd, DialogService dialogService) throws IllegalAccessException, InvocationTargetException, BootstrapException, CreationException, IOException, ClassNotFoundException { + public OOBibBase(Path loPath, DialogService dialogService) + throws + BootstrapException, + CreationException { this.dialogService = dialogService; + this.connection = new OOBibBaseConnect(loPath, dialogService); - authorYearTitleList.add(authComp); - authorYearTitleList.add(yearComp); - authorYearTitleList.add(titleComp); + this.refreshBibliographyDuringSyncWhenCiting = false; + this.alwaysAddCitedOnPages = false; + } - yearAuthorTitleList.add(yearComp); - yearAuthorTitleList.add(authComp); - yearAuthorTitleList.add(titleComp); + public void guiActionSelectDocument(boolean autoSelectForSingle) { + final String errorTitle = Localization.lang("Problem connecting"); - entryComparator = new FieldComparatorStack<>(authorYearTitleList); - yearAuthorTitleComparator = new FieldComparatorStack<>(yearAuthorTitleList); + try { - this.atEnd = atEnd; + this.connection.selectDocument(autoSelectForSingle); + } catch (NoDocumentFoundException ex) { + OOError.from(ex).showErrorDialog(dialogService); + } catch (DisposedException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (WrappedTargetException + | IndexOutOfBoundsException + | NoSuchElementException ex) { + LOGGER.warn("Problem connecting", ex); + OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } - xDesktop = simpleBootstrap(loPath); + if (this.isConnectedToDocument()) { + dialogService.notify(Localization.lang("Connected to document") + ": " + + this.getCurrentDocumentTitle().orElse("")); + } } + /** + * A simple test for document availability. + *

+ * See also `isDocumentConnectionMissing` for a test actually attempting to use the connection. + */ public boolean isConnectedToDocument() { - return xCurrentComponent != null; + return this.connection.isConnectedToDocument(); + } + + /** + * @return true if we are connected to a document + */ + public boolean isDocumentConnectionMissing() { + return this.connection.isDocumentConnectionMissing(); } - public XTextDocument selectComponent(List list) { - List viewModel = list.stream().map(DocumentTitleViewModel::new).collect(Collectors.toList()); - // this whole method is part of a background task when auto-detecting instances, so we need to show dialog in FX thread - Optional selectedDocument = dialogService.showChoiceDialogAndWait(Localization.lang("Select document"), Localization.lang("Found documents:"), Localization.lang("Use selected document"), viewModel); - return selectedDocument.map(DocumentTitleViewModel::getXtextDocument).orElse(null); + /** + * Either return an XTextDocument or return JabRefException. + */ + public OOResult getXTextDocument() { + return this.connection.getXTextDocument(); } + /** + * The title of the current document, or Optional.empty() + */ public Optional getCurrentDocumentTitle() { - return getDocumentTitle(mxDoc); + return this.connection.getCurrentDocumentTitle(); } - private Optional getDocumentTitle(XTextDocument doc) { - if (doc == null) { - return Optional.empty(); - } else { - try { - return Optional.of(String.valueOf(OOUtil.getProperty(doc.getCurrentController().getFrame(), "Title"))); - } catch (UnknownPropertyException | WrappedTargetException e) { - LOGGER.warn("Could not get document title", e); - return Optional.empty(); - } - } + /* ****************************************************** + * + * Tools to collect and show precondition test results + * + * ******************************************************/ + + void showDialog(OOError err) { + err.showErrorDialog(dialogService); } - public void selectDocument() throws NoDocumentException, NoSuchElementException, WrappedTargetException { - List textDocumentList = getTextDocuments(); - XTextDocument selected; - if (textDocumentList.isEmpty()) { - // No text documents found. - throw new NoDocumentException("No Writer documents found"); - } else if (textDocumentList.size() == 1) { - // Get the only one - selected = textDocumentList.get(0); - } else { - // Bring up a dialog - selected = selectComponent(textDocumentList); - } + void showDialog(String errorTitle, OOError err) { + err.setTitle(errorTitle).showErrorDialog(dialogService); + } - if (selected == null) { - return; + OOVoidResult collectResults(String errorTitle, List> results) { + String msg = (results.stream() + .filter(OOVoidResult::isError) + .map(e -> e.getError().getLocalizedMessage()) + .collect(Collectors.joining("\n\n"))); + if (msg.isEmpty()) { + return OOVoidResult.ok(); + } else { + return OOVoidResult.error(new OOError(errorTitle, msg)); } - xCurrentComponent = UnoRuntime.queryInterface(XComponent.class, selected); - mxDoc = selected; - - UnoRuntime.queryInterface(XDocumentIndexesSupplier.class, xCurrentComponent); + } - XModel xModel = UnoRuntime.queryInterface(XModel.class, xCurrentComponent); - XController xController = xModel.getCurrentController(); - xViewCursorSupplier = UnoRuntime.queryInterface(XTextViewCursorSupplier.class, xController); + boolean testDialog(OOVoidResult res) { + return res.ifError(ex -> ex.showErrorDialog(dialogService)).isError(); + } - // get a reference to the body text of the document - text = mxDoc.getText(); + boolean testDialog(String errorTitle, OOVoidResult res) { + return res.ifError(e -> showDialog(e.setTitle(errorTitle))).isError(); + } - // Access the text document's multi service factory: - mxDocFactory = UnoRuntime.queryInterface(XMultiServiceFactory.class, mxDoc); + boolean testDialog(String errorTitle, List> results) { + return testDialog(errorTitle, collectResults(errorTitle, results)); + } - XDocumentPropertiesSupplier supp = UnoRuntime.queryInterface(XDocumentPropertiesSupplier.class, mxDoc); - userProperties = supp.getDocumentProperties().getUserDefinedProperties(); - propertySet = UnoRuntime.queryInterface(XPropertySet.class, userProperties); + @SafeVarargs + final boolean testDialog(String errorTitle, OOVoidResult... results) { + List> resultList = Arrays.asList(results); + return testDialog(collectResults(errorTitle, resultList)); } - private List getTextDocuments() throws NoSuchElementException, WrappedTargetException { - List result = new ArrayList<>(); - XEnumerationAccess enumAccess = xDesktop.getComponents(); - XEnumeration componentEnumeration = enumAccess.createEnumeration(); + /** + * Get the cursor positioned by the user for inserting text. + */ + OOResult getUserCursorForTextInsertion(XTextDocument doc, String errorTitle) { - // TODO: http://api.openoffice.org/docs/DevelopersGuide/OfficeDev/OfficeDev.xhtml#1_1_3_2_1_2_Frame_Hierarchies + // Get the cursor positioned by the user. + XTextCursor cursor = UnoCursor.getViewCursor(doc).orElse(null); - while (componentEnumeration.hasMoreElements()) { - Object nextElement = componentEnumeration.nextElement(); - XComponent component = UnoRuntime.queryInterface(XComponent.class, nextElement); - XTextDocument document = UnoRuntime.queryInterface(XTextDocument.class, component); - if (document != null) { - result.add(document); - } + // Check for crippled XTextViewCursor + Objects.requireNonNull(cursor); + try { + cursor.getStart(); + } catch (com.sun.star.uno.RuntimeException ex) { + String msg = + Localization.lang("Please move the cursor" + + " to the location for the new citation.") + "\n" + + Localization.lang("I cannot insert to the cursors current location."); + return OOResult.error(new OOError(errorTitle, msg, ex)); } - return result; + return OOResult.ok(cursor); } - private XDesktop simpleBootstrap(Path loPath) - throws CreationException, BootstrapException { - - // Get the office component context: - XComponentContext xContext = org.jabref.gui.openoffice.Bootstrap.bootstrap(loPath); - // Get the office service manager: - XMultiComponentFactory xServiceManager = xContext.getServiceManager(); - // Create the desktop, which is the root frame of the - // hierarchy of frames that contain viewable components: - Object desktop; + /** + * This may move the view cursor. + */ + OOResult getFunctionalTextViewCursor(XTextDocument doc, String errorTitle) { + String messageOnFailureToObtain = + Localization.lang("Please move the cursor into the document text.") + + "\n" + + Localization.lang("To get the visual positions of your citations" + + " I need to move the cursor around," + + " but could not get it."); + OOResult result = FunctionalTextViewCursor.get(doc); + if (result.isError()) { + LOGGER.warn(result.getError()); + } + return result.mapError(detail -> new OOError(errorTitle, messageOnFailureToObtain)); + } + + private static OOVoidResult checkRangeOverlaps(XTextDocument doc, OOFrontend frontend) { + final String errorTitle = "Overlapping ranges"; + boolean requireSeparation = false; + int maxReportedOverlaps = 10; try { - desktop = xServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", xContext); - } catch (Exception e) { - throw new CreationException(e.getMessage()); + return (frontend.checkRangeOverlaps(doc, + new ArrayList<>(), + requireSeparation, + maxReportedOverlaps) + .mapError(OOError::from)); + } catch (NoDocumentException ex) { + return OOVoidResult.error(OOError.from(ex).setTitle(errorTitle)); + } catch (WrappedTargetException ex) { + return OOVoidResult.error(OOError.fromMisc(ex).setTitle(errorTitle)); } - XDesktop resultDesktop = UnoRuntime.queryInterface(XDesktop.class, desktop); - - UnoRuntime.queryInterface(XComponentLoader.class, desktop); - - return resultDesktop; } - public Optional getCustomProperty(String property) throws UnknownPropertyException, WrappedTargetException { - if (propertySet.getPropertySetInfo().hasPropertyByName(property)) { - return Optional.ofNullable(propertySet.getPropertyValue(property).toString()); - } - return Optional.empty(); - } + private static OOVoidResult checkRangeOverlapsWithCursor(XTextDocument doc, OOFrontend frontend) { + final String errorTitle = "Ranges overlapping with cursor"; - public void updateSortedReferenceMarks() throws WrappedTargetException, NoSuchElementException { - sortedReferenceMarks = getSortedReferenceMarks(getReferenceMarks()); - } + List> userRanges; + userRanges = frontend.viewCursorRanges(doc); - public void setCustomProperty(String property, String value) throws UnknownPropertyException, - NotRemoveableException, PropertyExistException, IllegalTypeException, IllegalArgumentException { - if (propertySet.getPropertySetInfo().hasPropertyByName(property)) { - userProperties.removeProperty(property); + boolean requireSeparation = false; + OOVoidResult res; + try { + res = frontend.checkRangeOverlapsWithCursor(doc, + userRanges, + requireSeparation); + } catch (NoDocumentException ex) { + return OOVoidResult.error(OOError.from(ex).setTitle(errorTitle)); + } catch (WrappedTargetException ex) { + return OOVoidResult.error(OOError.fromMisc(ex).setTitle(errorTitle)); } - if (value != null) { - userProperties.addProperty(property, com.sun.star.beans.PropertyAttribute.REMOVEABLE, - new Any(Type.STRING, value)); + + if (res.isError()) { + final String xtitle = Localization.lang("The cursor is in a protected area."); + return OOVoidResult.error( + new OOError(xtitle, xtitle + "\n" + res.getError().getLocalizedMessage() + "\n")); } + return res.mapError(OOError::from); } - /** - * This method inserts a cite marker in the text for the given BibEntry, and may refresh the bibliography. + /* ****************************************************** * - * @param entries The entries to cite. - * @param database The database the entry belongs to. - * @param style The bibliography style we are using. - * @param inParenthesis Indicates whether it is an in-text citation or a citation in parenthesis. This is not relevant if numbered citations are used. - * @param withText Indicates whether this should be a normal citation (true) or an empty (invisible) citation (false). - * @param sync Indicates whether the reference list should be refreshed. - * @throws IllegalTypeException - * @throws PropertyExistException - * @throws NotRemoveableException - * @throws UnknownPropertyException - * @throws UndefinedCharacterFormatException - * @throws NoSuchElementException - * @throws WrappedTargetException - * @throws IOException - * @throws PropertyVetoException - * @throws CreationException - * @throws BibEntryNotFoundException - * @throws UndefinedParagraphFormatException - */ - public void insertEntry(List entries, BibDatabase database, - List allBases, OOBibStyle style, - boolean inParenthesis, boolean withText, String pageInfo, boolean sync) - throws IllegalArgumentException, - UnknownPropertyException, NotRemoveableException, PropertyExistException, IllegalTypeException, - UndefinedCharacterFormatException, WrappedTargetException, NoSuchElementException, PropertyVetoException, - IOException, CreationException, BibEntryNotFoundException, UndefinedParagraphFormatException { - - try { + * Tests for preconditions. + * + * ******************************************************/ - XTextViewCursor xViewCursor = xViewCursorSupplier.getViewCursor(); + private static OOVoidResult checkIfOpenOfficeIsRecordingChanges(XTextDocument doc) { - if (entries.size() > 1) { - if (style.getBooleanCitProperty(OOBibStyle.MULTI_CITE_CHRONOLOGICAL)) { - entries.sort(yearAuthorTitleComparator); - } else { - entries.sort(entryComparator); + String errorTitle = Localization.lang("Recording and/or Recorded changes"); + try { + boolean recordingChanges = UnoRedlines.getRecordChanges(doc); + int nRedlines = UnoRedlines.countRedlines(doc); + if (recordingChanges || (nRedlines > 0)) { + String msg = ""; + if (recordingChanges) { + msg += Localization.lang("Cannot work with" + + " [Edit]/[Track Changes]/[Record] turned on."); } - } - - String keyString = String.join(",", - entries.stream().map(entry -> entry.getCitationKey().orElse("")).collect(Collectors.toList())); - // Insert bookmark: - String bName = getUniqueReferenceMarkName(keyString, - withText ? inParenthesis ? OOBibBase.AUTHORYEAR_PAR : OOBibBase.AUTHORYEAR_INTEXT : OOBibBase.INVISIBLE_CIT); - - // If we should store metadata for page info, do that now: - if (pageInfo != null) { - LOGGER.info("Storing page info: " + pageInfo); - setCustomProperty(bName, pageInfo); - } - - xViewCursor.getText().insertString(xViewCursor, " ", false); - if (style.isFormatCitations()) { - XPropertySet xCursorProps = UnoRuntime.queryInterface(XPropertySet.class, xViewCursor); - String charStyle = style.getCitationCharacterFormat(); - try { - xCursorProps.setPropertyValue(CHAR_STYLE_NAME, charStyle); - } catch (UnknownPropertyException | PropertyVetoException | IllegalArgumentException | - WrappedTargetException ex) { - // Setting the character format failed, so we throw an exception that - // will result in an error message for the user. Before that, - // delete the space we inserted: - xViewCursor.goLeft((short) 1, true); - xViewCursor.setString(""); - throw new UndefinedCharacterFormatException(charStyle); + if (nRedlines > 0) { + if (recordingChanges) { + msg += "\n"; + } + msg += Localization.lang("Changes by JabRef" + + " could result in unexpected interactions with" + + " recorded changes."); + msg += "\n"; + msg += Localization.lang("Use [Edit]/[Track Changes]/[Manage] to resolve them first."); } + return OOVoidResult.error(new OOError(errorTitle, msg)); } - xViewCursor.goLeft((short) 1, false); - Map databaseMap = new HashMap<>(); - for (BibEntry entry : entries) { - databaseMap.put(entry, database); - } - String citeText = style.isNumberEntries() ? "-" : style.getCitationMarker(entries, databaseMap, - inParenthesis, null, null); - insertReferenceMark(bName, citeText, xViewCursor, withText, style); - - xViewCursor.collapseToEnd(); - xViewCursor.goRight((short) 1, false); - - XTextRange position = xViewCursor.getEnd(); - - if (sync) { - // To account for numbering and for uniqiefiers, we must refresh the cite markers: - updateSortedReferenceMarks(); - refreshCiteMarkers(allBases, style); - - // Insert it at the current position: - rebuildBibTextSection(allBases, style); - } - - // Go back to the relevant position: - xViewCursor.gotoRange(position, false); - } catch (DisposedException ex) { - // We need to catch this one here because the OpenOfficePanel class is - // loaded before connection, and therefore cannot directly reference - // or catch a DisposedException (which is in a OO JAR file). - throw new ConnectionLostException(ex.getMessage()); + } catch (WrappedTargetException ex) { + String msg = Localization.lang("Error while checking if Writer" + + " is recording changes or has recorded changes."); + return OOVoidResult.error(new OOError(errorTitle, msg, ex)); } + return OOVoidResult.ok(); } - public List getJabRefReferenceMarks(XNameAccess nameAccess) { - String[] names = nameAccess.getElementNames(); - // Remove all reference marks that don't look like JabRef citations: - List result = new ArrayList<>(); - if (names != null) { - for (String name : names) { - if (CITE_PATTERN.matcher(name).find()) { - result.add(name); - } - } + OOVoidResult styleIsRequired(OOBibStyle style) { + if (style == null) { + return OOVoidResult.error(OOError.noValidStyleSelected()); + } else { + return OOVoidResult.ok(); } - return result; } - /** - * Refresh all cite markers in the document. - * - * @param databases The databases to get entries from. - * @param style The bibliography style to use. - * @return A list of those referenced citation keys that could not be resolved. - * @throws UndefinedCharacterFormatException - * @throws NoSuchElementException - * @throws IllegalArgumentException - * @throws WrappedTargetException - * @throws BibEntryNotFoundException - * @throws CreationException - * @throws IOException - * @throws PropertyVetoException - * @throws UnknownPropertyException - */ - public List refreshCiteMarkers(List databases, OOBibStyle style) - throws WrappedTargetException, IllegalArgumentException, NoSuchElementException, - UndefinedCharacterFormatException, UnknownPropertyException, PropertyVetoException, IOException, - CreationException, BibEntryNotFoundException { + OOResult getFrontend(XTextDocument doc) { + final String errorTitle = "Unable to get frontend"; try { - return refreshCiteMarkersInternal(databases, style); - } catch (DisposedException ex) { - // We need to catch this one here because the OpenOfficePanel class is - // loaded before connection, and therefore cannot directly reference - // or catch a DisposedException (which is in a OO JAR file). - throw new ConnectionLostException(ex.getMessage()); + return OOResult.ok(new OOFrontend(doc)); + } catch (NoDocumentException ex) { + return OOResult.error(OOError.from(ex).setTitle(errorTitle)); + } catch (WrappedTargetException + | RuntimeException ex) { + return OOResult.error(OOError.fromMisc(ex).setTitle(errorTitle)); } } - private List refreshCiteMarkersInternal(List databases, OOBibStyle style) - throws WrappedTargetException, IllegalArgumentException, NoSuchElementException, - UndefinedCharacterFormatException, UnknownPropertyException, PropertyVetoException, - CreationException, BibEntryNotFoundException { - - List cited = findCitedKeys(); - Map linkSourceBase = new HashMap<>(); - Map entries = findCitedEntries(databases, cited, linkSourceBase); - - XNameAccess xReferenceMarks = getReferenceMarks(); - - List names; - if (style.isSortByPosition()) { - // We need to sort the reference marks according to their order of appearance: - names = sortedReferenceMarks; - } else if (style.isNumberEntries()) { - // We need to sort the reference marks according to the sorting of the bibliographic - // entries: - SortedMap newMap = new TreeMap<>(entryComparator); - for (Map.Entry bibtexEntryBibtexDatabaseEntry : entries.entrySet()) { - newMap.put(bibtexEntryBibtexDatabaseEntry.getKey(), bibtexEntryBibtexDatabaseEntry.getValue()); - } - entries = newMap; - // Rebuild the list of cited keys according to the sort order: - cited.clear(); - for (BibEntry entry : entries.keySet()) { - cited.add(entry.getCitationKey().orElse(null)); - } - names = Arrays.asList(xReferenceMarks.getElementNames()); + OOVoidResult databaseIsRequired(List databases, + Supplier fun) { + if (databases == null || databases.isEmpty()) { + return OOVoidResult.error(fun.get()); } else { - names = sortedReferenceMarks; - } - - // Remove all reference marks that don't look like JabRef citations: - List tmp = new ArrayList<>(); - for (String name : names) { - if (CITE_PATTERN.matcher(name).find()) { - tmp.add(name); - } + return OOVoidResult.ok(); } - names = tmp; - - Map numbers = new HashMap<>(); - int lastNum = 0; - // First compute citation markers for all citations: - String[] citMarkers = new String[names.size()]; - String[][] normCitMarkers = new String[names.size()][]; - String[][] bibtexKeys = new String[names.size()][]; - - int minGroupingCount = style.getIntCitProperty(OOBibStyle.MINIMUM_GROUPING_COUNT); - - int[] types = new int[names.size()]; - for (int i = 0; i < names.size(); i++) { - Matcher citeMatcher = CITE_PATTERN.matcher(names.get(i)); - if (citeMatcher.find()) { - String typeStr = citeMatcher.group(1); - int type = Integer.parseInt(typeStr); - types[i] = type; // Remember the type in case we need to uniquefy. - String[] keys = citeMatcher.group(2).split(","); - bibtexKeys[i] = keys; - BibEntry[] cEntries = new BibEntry[keys.length]; - for (int j = 0; j < cEntries.length; j++) { - BibDatabase database = linkSourceBase.get(keys[j]); - Optional tmpEntry = Optional.empty(); - if (database != null) { - tmpEntry = database.getEntryByCitationKey(keys[j]); - } - if (tmpEntry.isPresent()) { - cEntries[j] = tmpEntry.get(); - } else { - LOGGER.info("Citation key not found: '" + keys[j] + '\''); - LOGGER.info("Problem with reference mark: '" + names.get(i) + '\''); - throw new BibEntryNotFoundException(names.get(i), Localization - .lang("Could not resolve BibTeX entry for citation marker '%0'.", names.get(i))); - } - } - - String[] normCitMarker = new String[keys.length]; - String citationMarker; - if (style.isCitationKeyCiteMarkers()) { - StringBuilder sb = new StringBuilder(); - normCitMarkers[i] = new String[keys.length]; - for (int j = 0; j < keys.length; j++) { - normCitMarkers[i][j] = cEntries[j].getCitationKey().orElse(null); - sb.append(cEntries[j].getCitationKey().orElse("")); - if (j < (keys.length - 1)) { - sb.append(','); - } - } - citationMarker = sb.toString(); - } else if (style.isNumberEntries()) { - if (style.isSortByPosition()) { - // We have sorted the citation markers according to their order of appearance, - // so we simply count up for each marker referring to a new entry: - List num = new ArrayList<>(keys.length); - for (int j = 0; j < keys.length; j++) { - if (cEntries[j] instanceof UndefinedBibtexEntry) { - num.add(j, -1); - } else { - num.add(j, lastNum + 1); - if (numbers.containsKey(keys[j])) { - num.set(j, numbers.get(keys[j])); - } else { - numbers.put(keys[j], num.get(j)); - lastNum = num.get(j); - } - } - } - citationMarker = style.getNumCitationMarker(num, minGroupingCount, false); - for (int j = 0; j < keys.length; j++) { - normCitMarker[j] = style.getNumCitationMarker(Collections.singletonList(num.get(j)), - minGroupingCount, false); - } - } else { - // We need to find the number of the cited entry in the bibliography, - // and use that number for the cite marker: - List num = findCitedEntryIndex(names.get(i), cited); - - if (num.isEmpty()) { - throw new BibEntryNotFoundException(names.get(i), Localization - .lang("Could not resolve BibTeX entry for citation marker '%0'.", names.get(i))); - } else { - citationMarker = style.getNumCitationMarker(num, minGroupingCount, false); - } - - for (int j = 0; j < keys.length; j++) { - List list = new ArrayList<>(1); - list.add(num.get(j)); - normCitMarker[j] = style.getNumCitationMarker(list, minGroupingCount, false); - } - } - } else { - - if (cEntries.length > 1) { - if (style.getBooleanCitProperty(OOBibStyle.MULTI_CITE_CHRONOLOGICAL)) { - Arrays.sort(cEntries, yearAuthorTitleComparator); - } else { - Arrays.sort(cEntries, entryComparator); - } - // Update key list to match the new sorting: - for (int j = 0; j < cEntries.length; j++) { - bibtexKeys[i][j] = cEntries[j].getCitationKey().orElse(null); - } - } + } - citationMarker = style.getCitationMarker(Arrays.asList(cEntries), entries, - type == OOBibBase.AUTHORYEAR_PAR, null, null); - // We need "normalized" (in parenthesis) markers for uniqueness checking purposes: - for (int j = 0; j < cEntries.length; j++) { - normCitMarker[j] = style.getCitationMarker(Collections.singletonList(cEntries[j]), entries, - true, null, new int[] {-1}); - } - } - citMarkers[i] = citationMarker; - normCitMarkers[i] = normCitMarker; - } + OOVoidResult selectedBibEntryIsRequired(List entries, + Supplier fun) { + if (entries == null || entries.isEmpty()) { + return OOVoidResult.error(fun.get()); + } else { + return OOVoidResult.ok(); } + } - uniquefiers.clear(); - if (!style.isCitationKeyCiteMarkers() && !style.isNumberEntries()) { - // See if there are duplicate citations marks referring to different entries. If so, we need to - // use uniquefiers: - Map> refKeys = new HashMap<>(); - Map> refNums = new HashMap<>(); - for (int i = 0; i < citMarkers.length; i++) { - String[] markers = normCitMarkers[i]; // compare normalized markers, since the actual markers can be different - for (int j = 0; j < markers.length; j++) { - String marker = markers[j]; - String currentKey = bibtexKeys[i][j]; - if (refKeys.containsKey(marker)) { - // Ok, we have seen this exact marker before. - if (!refKeys.get(marker).contains(currentKey)) { - // ... but not for this entry. - refKeys.get(marker).add(currentKey); - refNums.get(marker).add(i); - } - } else { - List l = new ArrayList<>(1); - l.add(currentKey); - refKeys.put(marker, l); - List l2 = new ArrayList<>(1); - l2.add(i); - refNums.put(marker, l2); - } - } - } - // Go through the collected lists and see where we need to uniquefy: - for (Map.Entry> stringListEntry : refKeys.entrySet()) { - List keys = stringListEntry.getValue(); - if (keys.size() > 1) { - // This marker appears for more than one unique entry: - int uniq = 'a'; - for (String key : keys) { - // Update the map of uniquefiers for the benefit of both the following generation of new - // citation markers, and for the method that builds the bibliography: - uniquefiers.put(key, String.valueOf((char) uniq)); - uniq++; - } - } - } - - // Finally, go through all citation markers, and update those referring to entries in our current list: - int maxAuthorsFirst = style.getIntCitProperty(OOBibStyle.MAX_AUTHORS_FIRST); - Set seenBefore = new HashSet<>(); - for (int j = 0; j < bibtexKeys.length; j++) { - boolean needsChange = false; - int[] firstLimAuthors = new int[bibtexKeys[j].length]; - String[] uniquif = new String[bibtexKeys[j].length]; - BibEntry[] cEntries = new BibEntry[bibtexKeys[j].length]; - for (int k = 0; k < bibtexKeys[j].length; k++) { - String currentKey = bibtexKeys[j][k]; - firstLimAuthors[k] = -1; - if (maxAuthorsFirst > 0) { - if (!seenBefore.contains(currentKey)) { - firstLimAuthors[k] = maxAuthorsFirst; - } - seenBefore.add(currentKey); - } - String uniq = uniquefiers.get(currentKey); - Optional tmpEntry = Optional.empty(); - if (uniq == null) { - if (firstLimAuthors[k] > 0) { - needsChange = true; - BibDatabase database = linkSourceBase.get(currentKey); - if (database != null) { - tmpEntry = database.getEntryByCitationKey(currentKey); - } - } else { - BibDatabase database = linkSourceBase.get(currentKey); - if (database != null) { - tmpEntry = database.getEntryByCitationKey(currentKey); - } - } - uniquif[k] = ""; - } else { - needsChange = true; - BibDatabase database = linkSourceBase.get(currentKey); - if (database != null) { - tmpEntry = database.getEntryByCitationKey(currentKey); - } - uniquif[k] = uniq; + /* + * Checks existence and also checks if it is not an internal name. + */ + private OOVoidResult checkStyleExistsInTheDocument(String familyName, + String styleName, + XTextDocument doc, + String labelInJstyleFile, + String pathToStyleFile) + throws + WrappedTargetException { + + Optional internalName = UnoStyle.getInternalNameOfStyle(doc, familyName, styleName); + + if (internalName.isEmpty()) { + String msg = + switch (familyName) { + case UnoStyle.PARAGRAPH_STYLES -> Localization.lang("The %0 paragraph style '%1' is missing from the document", + labelInJstyleFile, + styleName); + case UnoStyle.CHARACTER_STYLES -> Localization.lang("The %0 character style '%1' is missing from the document", + labelInJstyleFile, + styleName); + default -> throw new IllegalArgumentException("Expected " + UnoStyle.CHARACTER_STYLES + + " or " + UnoStyle.PARAGRAPH_STYLES + + " for familyName"); } - if (tmpEntry.isPresent()) { - cEntries[k] = tmpEntry.get(); + + "\n" + + Localization.lang("Please create it in the document or change in the file:") + + "\n" + + pathToStyleFile; + return OOVoidResult.error(new OOError("StyleIsNotKnown", msg)); + } + + if (!internalName.get().equals(styleName)) { + String msg = + switch (familyName) { + case UnoStyle.PARAGRAPH_STYLES -> Localization.lang("The %0 paragraph style '%1' is a display name for '%2'.", + labelInJstyleFile, + styleName, + internalName.get()); + case UnoStyle.CHARACTER_STYLES -> Localization.lang("The %0 character style '%1' is a display name for '%2'.", + labelInJstyleFile, + styleName, + internalName.get()); + default -> throw new IllegalArgumentException("Expected " + UnoStyle.CHARACTER_STYLES + + " or " + UnoStyle.PARAGRAPH_STYLES + + " for familyName"); } - } - if (needsChange) { - citMarkers[j] = style.getCitationMarker(Arrays.asList(cEntries), entries, - types[j] == OOBibBase.AUTHORYEAR_PAR, uniquif, firstLimAuthors); - } - } + + "\n" + + Localization.lang("Please use the latter in the style file below" + + " to avoid localization problems.") + + "\n" + + pathToStyleFile; + return OOVoidResult.error(new OOError("StyleNameIsNotInternal", msg)); } + return OOVoidResult.ok(); + } - // Refresh all reference marks with the citation markers we computed: - boolean hadBibSection = getBookmarkRange(OOBibBase.BIB_SECTION_NAME) != null; - // Check if we are supposed to set a character format for citations: - boolean mustTestCharFormat = style.isFormatCitations(); - for (int i = 0; i < names.size(); i++) { - Object referenceMark = xReferenceMarks.getByName(names.get(i)); - XTextContent bookmark = UnoRuntime.queryInterface(XTextContent.class, referenceMark); - - XTextCursor cursor = bookmark.getAnchor().getText().createTextCursorByRange(bookmark.getAnchor()); - - if (mustTestCharFormat) { - // If we are supposed to set character format for citations, must run a test before we - // delete old citation markers. Otherwise, if the specified character format doesn't - // exist, we end up deleting the markers before the process crashes due to a the missing - // format, with catastrophic consequences for the user. - mustTestCharFormat = false; // need to do this only once - XPropertySet xCursorProps = UnoRuntime.queryInterface(XPropertySet.class, cursor); - String charStyle = style.getCitationCharacterFormat(); - try { - xCursorProps.setPropertyValue(CHAR_STYLE_NAME, charStyle); - } catch (UnknownPropertyException | PropertyVetoException | IllegalArgumentException | - WrappedTargetException ex) { - throw new UndefinedCharacterFormatException(charStyle); - } - } + public OOVoidResult checkStylesExistInTheDocument(OOBibStyle style, XTextDocument doc) { - text.removeTextContent(bookmark); + String pathToStyleFile = style.getPath(); - insertReferenceMark(names.get(i), citMarkers[i], cursor, types[i] != OOBibBase.INVISIBLE_CIT, style); - if (hadBibSection && (getBookmarkRange(OOBibBase.BIB_SECTION_NAME) == null)) { - // We have overwritten the marker for the start of the reference list. - // We need to add it again. - cursor.collapseToEnd(); - OOUtil.insertParagraphBreak(text, cursor); - insertBookMark(OOBibBase.BIB_SECTION_NAME, cursor); + List> results = new ArrayList<>(); + try { + results.add(checkStyleExistsInTheDocument(UnoStyle.PARAGRAPH_STYLES, + style.getReferenceHeaderParagraphFormat(), + doc, + "ReferenceHeaderParagraphFormat", + pathToStyleFile)); + results.add(checkStyleExistsInTheDocument(UnoStyle.PARAGRAPH_STYLES, + style.getReferenceParagraphFormat(), + doc, + "ReferenceParagraphFormat", + pathToStyleFile)); + if (style.isFormatCitations()) { + results.add(checkStyleExistsInTheDocument(UnoStyle.CHARACTER_STYLES, + style.getCitationCharacterFormat(), + doc, + "CitationCharacterFormat", + pathToStyleFile)); } + } catch (WrappedTargetException ex) { + results.add(OOVoidResult.error(new OOError("Other error in checkStyleExistsInTheDocument", + ex.getMessage(), + ex))); } - List unresolvedKeys = new ArrayList<>(); - for (BibEntry entry : entries.keySet()) { - if (entry instanceof UndefinedBibtexEntry) { - String key = ((UndefinedBibtexEntry) entry).getKey(); - if (!unresolvedKeys.contains(key)) { - unresolvedKeys.add(key); - } - } - } - return unresolvedKeys; + return collectResults("checkStyleExistsInTheDocument failed", results); } - private List getSortedReferenceMarks(final XNameAccess nameAccess) - throws WrappedTargetException, NoSuchElementException { - XTextViewCursorSupplier cursorSupplier = UnoRuntime.queryInterface(XTextViewCursorSupplier.class, - mxDoc.getCurrentController()); - - XTextViewCursor viewCursor = cursorSupplier.getViewCursor(); - XTextRange initialPos = viewCursor.getStart(); - List names = Arrays.asList(nameAccess.getElementNames()); - List positions = new ArrayList<>(names.size()); - for (String name : names) { - XTextContent textContent = UnoRuntime.queryInterface(XTextContent.class, nameAccess.getByName(name)); - XTextRange range = textContent.getAnchor(); - // Check if we are inside a footnote: - if (UnoRuntime.queryInterface(XFootnote.class, range.getText()) != null) { - // Find the linking footnote marker: - XFootnote footer = UnoRuntime.queryInterface(XFootnote.class, range.getText()); - // The footnote's anchor gives the correct position in the text: - range = footer.getAnchor(); - } + /* ****************************************************** + * + * ManageCitationsDialogView + * + * ******************************************************/ + public Optional> guiActionGetCitationEntries() { - positions.add(findPosition(viewCursor, range)); - } - Set set = new TreeSet<>(); - for (int i = 0; i < positions.size(); i++) { - set.add(new ComparableMark(names.get(i), positions.get(i))); - } + final Optional> FAIL = Optional.empty(); + final String errorTitle = Localization.lang("Problem collecting citations"); - List result = new ArrayList<>(set.size()); - for (ComparableMark mark : set) { - result.add(mark.getName()); + OOResult odoc = getXTextDocument(); + if (testDialog(errorTitle, odoc.asVoidResult())) { + return FAIL; } - viewCursor.gotoRange(initialPos, false); - - return result; - } - - public XNameAccess getReferenceMarks() { - XReferenceMarksSupplier supplier = UnoRuntime.queryInterface(XReferenceMarksSupplier.class, xCurrentComponent); - return supplier.getReferenceMarks(); - } + XTextDocument doc = odoc.get(); - private String getUniqueReferenceMarkName(String bibtexKey, int type) { - XNameAccess xNamedRefMarks = getReferenceMarks(); - int i = 0; - String name = OOBibBase.BIB_CITATION + '_' + type + '_' + bibtexKey; - while (xNamedRefMarks.hasByName(name)) { - name = OOBibBase.BIB_CITATION + i + '_' + type + '_' + bibtexKey; - i++; + if (testDialog(errorTitle, checkIfOpenOfficeIsRecordingChanges(doc))) { + LOGGER.warn(errorTitle); + return FAIL; } - return name; - } - public void rebuildBibTextSection(List databases, OOBibStyle style) - throws NoSuchElementException, WrappedTargetException, IllegalArgumentException, - CreationException, PropertyVetoException, UnknownPropertyException, UndefinedParagraphFormatException { - List cited = findCitedKeys(); - Map linkSourceBase = new HashMap<>(); - Map entries = findCitedEntries(databases, cited, linkSourceBase); // Although entries are redefined without use, this also updates linkSourceBase - - List names = sortedReferenceMarks; + try { - if (style.isSortByPosition()) { - // We need to sort the entries according to their order of appearance: - entries = getSortedEntriesFromSortedRefMarks(names, linkSourceBase); - } else { - SortedMap newMap = new TreeMap<>(entryComparator); - for (Map.Entry bibtexEntryBibtexDatabaseEntry : findCitedEntries(databases, cited, - linkSourceBase).entrySet()) { - newMap.put(bibtexEntryBibtexDatabaseEntry.getKey(), bibtexEntryBibtexDatabaseEntry.getValue()); - } - entries = newMap; + return Optional.of(ManageCitations.getCitationEntries(doc)); + } catch (NoDocumentException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + return FAIL; + } catch (DisposedException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + return FAIL; + } catch (WrappedTargetException ex) { + LOGGER.warn(errorTitle, ex); + OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); + return FAIL; } - clearBibTextSectionContent2(); - populateBibTextSection(entries, style); } - private List findCitedKeys() throws NoSuchElementException, WrappedTargetException { - XNameAccess xNamedMarks = getReferenceMarks(); - String[] names = xNamedMarks.getElementNames(); - List keys = new ArrayList<>(); - for (String name1 : names) { - Object bookmark = xNamedMarks.getByName(name1); - UnoRuntime.queryInterface(XTextContent.class, bookmark); - - List newKeys = parseRefMarkName(name1); - for (String key : newKeys) { - if (!keys.contains(key)) { - keys.add(key); - } - } - } + /** + * Apply editable parts of citationEntries to the document: store pageInfo. + *

+ * Does not change presentation. + *

+ * Note: we use no undo context here, because only DocumentConnection.setUserDefinedStringPropertyValue() is called, and Undo in LO will not undo that. + *

+ * GUI: "Manage citations" dialog "OK" button. Called from: ManageCitationsDialogViewModel.storeSettings + * + *

+ * Currently the only editable part is pageInfo. + *

+ * Since the only call to applyCitationEntries() only changes pageInfo w.r.t those returned by getCitationEntries(), we can do with the following restrictions: + *

    + *
  • Missing pageInfo means no action.
  • + *
  • Missing CitationEntry means no action (no attempt to remove + * citation from the text).
  • + *
+ */ + public void guiActionApplyCitationEntries(List citationEntries) { - return keys; - } + final String errorTitle = Localization.lang("Problem modifying citation"); - private Map findCitedEntries(List databases, List keys, - Map linkSourceBase) { - Map entries = new LinkedHashMap<>(); - for (String key : keys) { - boolean found = false; - for (BibDatabase database : databases) { - Optional entry = database.getEntryByCitationKey(key); - if (entry.isPresent()) { - entries.put(entry.get(), database); - linkSourceBase.put(key, database); - found = true; - break; - } - } - - if (!found) { - entries.put(new UndefinedBibtexEntry(key), null); - } + OOResult odoc = getXTextDocument(); + if (testDialog(errorTitle, odoc.asVoidResult())) { + return; } - return entries; - } + XTextDocument doc = odoc.get(); - private Point findPosition(XTextViewCursor cursor, XTextRange range) { - cursor.gotoRange(range, false); - return cursor.getPosition(); - } + try { - /** - * Extract the list of citation keys from a reference mark name. - * - * @param name The reference mark name. - * @return The list of citation keys encoded in the name. - */ - public List parseRefMarkName(String name) { - List keys = new ArrayList<>(); - Matcher citeMatcher = CITE_PATTERN.matcher(name); - if (citeMatcher.find()) { - String[] keystring = citeMatcher.group(2).split(","); - for (String aKeystring : keystring) { - if (!keys.contains(aKeystring)) { - keys.add(aKeystring); - } - } + ManageCitations.applyCitationEntries(doc, citationEntries); + } catch (NoDocumentException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (DisposedException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (PropertyVetoException + | IllegalTypeException + | WrappedTargetException + | com.sun.star.lang.IllegalArgumentException ex) { + LOGGER.warn(errorTitle, ex); + OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); } - return keys; } /** - * Resolve the citation key from a citation reference marker name, and look up the index of the key in a list of keys. + * Creates a citation group from {@code entries} at the cursor. + *

+ * Uses LO undo context "Insert citation". + *

+ * Note: Undo does not remove or reestablish custom properties. * - * @param citRefName The name of the ReferenceMark representing the citation. - * @param keys A List of citation keys representing the entries in the bibliography. - * @return the indices of the cited keys, -1 if a key is not found. Returns null if the ref name could not be resolved as a citation. + * @param entries The entries to cite. + * @param database The database the entries belong to (all of them). Used when creating the citation mark. + *

+ * Consistency: for each entry in {@code entries}: looking it up in {@code syncOptions.get().databases} (if present) should yield {@code database}. + * @param style The bibliography style we are using. + * @param citationType Indicates whether it is an in-text citation, a citation in parenthesis or an invisible citation. + * @param pageInfo A single page-info for these entries. Attributed to the last entry. + * @param syncOptions Indicates whether in-text citations should be refreshed in the document. Optional.empty() indicates no refresh. Otherwise provides options for refreshing the reference list. */ - private List findCitedEntryIndex(String citRefName, List keys) { - Matcher citeMatcher = CITE_PATTERN.matcher(citRefName); - if (citeMatcher.find()) { - List keyStrings = Arrays.asList(citeMatcher.group(2).split(",")); - List result = new ArrayList<>(keyStrings.size()); - for (String key : keyStrings) { - int ind = keys.indexOf(key); - result.add(ind == -1 ? -1 : 1 + ind); - } - return result; - } else { - return Collections.emptyList(); + public void guiActionInsertEntry(List entries, + BibDatabase database, + OOBibStyle style, + CitationType citationType, + String pageInfo, + Optional syncOptions) { + + final String errorTitle = "Could not insert citation"; + + OOResult odoc = getXTextDocument(); + if (testDialog(errorTitle, + odoc.asVoidResult(), + styleIsRequired(style), + selectedBibEntryIsRequired(entries, OOError::noEntriesSelectedForCitation))) { + return; } - } + XTextDocument doc = odoc.get(); - private Map getSortedEntriesFromSortedRefMarks(List names, - Map linkSourceBase) { - - Map newList = new LinkedHashMap<>(); - for (String name : names) { - Matcher citeMatcher = CITE_PATTERN.matcher(name); - if (citeMatcher.find()) { - String[] keys = citeMatcher.group(2).split(","); - for (String key : keys) { - BibDatabase database = linkSourceBase.get(key); - Optional origEntry = Optional.empty(); - if (database != null) { - origEntry = database.getEntryByCitationKey(key); - } - if (origEntry.isPresent()) { - if (!newList.containsKey(origEntry.get())) { - newList.put(origEntry.get(), database); - } - } else { - LOGGER.info("Citation key not found: '" + key + "'"); - LOGGER.info("Problem with reference mark: '" + name + "'"); - newList.put(new UndefinedBibtexEntry(key), null); - } - } - } + OOResult frontend = getFrontend(doc); + if (testDialog(errorTitle, frontend.asVoidResult())) { + return; } - return newList; - } - - public String getCitationContext(XNameAccess nameAccess, String refMarkName, int charBefore, int charAfter, - boolean htmlMarkup) - throws NoSuchElementException, WrappedTargetException { - Object referenceMark = nameAccess.getByName(refMarkName); - XTextContent bookmark = UnoRuntime.queryInterface(XTextContent.class, referenceMark); - - XTextCursor cursor = bookmark.getAnchor().getText().createTextCursorByRange(bookmark.getAnchor()); - String citPart = cursor.getString(); - int flex = 8; - for (int i = 0; i < charBefore; i++) { - try { - cursor.goLeft((short) 1, true); - if ((i >= (charBefore - flex)) && Character.isWhitespace(cursor.getString().charAt(0))) { - break; - } - } catch (IndexOutOfBoundsException ex) { - LOGGER.warn("Problem going left", ex); - } - } - int length = cursor.getString().length(); - int added = length - citPart.length(); - cursor.collapseToStart(); - for (int i = 0; i < (charAfter + length); i++) { - try { - cursor.goRight((short) 1, true); - if (i >= ((charAfter + length) - flex)) { - String strNow = cursor.getString(); - if (Character.isWhitespace(strNow.charAt(strNow.length() - 1))) { - break; - } - } - } catch (IndexOutOfBoundsException ex) { - LOGGER.warn("Problem going right", ex); - } + OOResult cursor = getUserCursorForTextInsertion(doc, errorTitle); + if (testDialog(errorTitle, cursor.asVoidResult())) { + return; } - String result = cursor.getString(); - if (htmlMarkup) { - result = result.substring(0, added) + "" + citPart + "" + result.substring(length); + if (testDialog(errorTitle, checkRangeOverlapsWithCursor(doc, frontend.get()))) { + return; } - return result.trim(); - } - private void insertFullReferenceAtCursor(XTextCursor cursor, Map entries, OOBibStyle style, - String parFormat) - throws UndefinedParagraphFormatException, IllegalArgumentException, - UnknownPropertyException, PropertyVetoException, WrappedTargetException { - Map correctEntries; - // If we don't have numbered entries, we need to sort the entries before adding them: - if (style.isSortByPosition()) { - // Use the received map directly - correctEntries = entries; - } else { - // Sort map - Map newMap = new TreeMap<>(entryComparator); - newMap.putAll(entries); - correctEntries = newMap; + if (testDialog(errorTitle, + checkStylesExistInTheDocument(style, doc), + checkIfOpenOfficeIsRecordingChanges(doc))) { + return; } - int number = 1; - for (Map.Entry entry : correctEntries.entrySet()) { - if (entry.getKey() instanceof UndefinedBibtexEntry) { - continue; - } - OOUtil.insertParagraphBreak(text, cursor); - if (style.isNumberEntries()) { - int minGroupingCount = style.getIntCitProperty(OOBibStyle.MINIMUM_GROUPING_COUNT); - OOUtil.insertTextAtCurrentLocation(text, cursor, - style.getNumCitationMarker(Collections.singletonList(number++), minGroupingCount, true), Collections.emptyList()); + + /* + * For sync we need a FunctionalTextViewCursor. + */ + OOResult fcursor = null; + if (syncOptions.isPresent()) { + fcursor = getFunctionalTextViewCursor(doc, errorTitle); + if (testDialog(errorTitle, fcursor.asVoidResult())) { + return; } - Layout layout = style.getReferenceFormat(entry.getKey().getType()); - layout.setPostFormatter(POSTFORMATTER); - OOUtil.insertFullReferenceAtCurrentLocation(text, cursor, layout, parFormat, entry.getKey(), - entry.getValue(), uniquefiers.get(entry.getKey().getCitationKey().orElse(null))); } - } - private void createBibTextSection2(boolean end) - throws IllegalArgumentException, CreationException { + syncOptions + .map(e -> e.setUpdateBibliography(this.refreshBibliographyDuringSyncWhenCiting)) + .map(e -> e.setAlwaysAddCitedOnPages(this.alwaysAddCitedOnPages)); - XTextCursor mxDocCursor = text.createTextCursor(); - if (end) { - mxDocCursor.gotoEnd(false); + if (syncOptions.isPresent()) { + if (testDialog(databaseIsRequired(syncOptions.get().databases, + OOError::noDataBaseIsOpenForSyncingAfterCitation))) { + return; + } } - OOUtil.insertParagraphBreak(text, mxDocCursor); - // Create a new TextSection from the document factory and access it's XNamed interface - XNamed xChildNamed; + try { - xChildNamed = UnoRuntime.queryInterface(XNamed.class, - mxDocFactory.createInstance("com.sun.star.text.TextSection")); - } catch (Exception e) { - throw new CreationException(e.getMessage()); + UnoUndo.enterUndoContext(doc, "Insert citation"); + + EditInsert.insertCitationGroup(doc, + frontend.get(), + cursor.get(), + entries, + database, + style, + citationType, + pageInfo); + + if (syncOptions.isPresent()) { + Update.resyncDocument(doc, style, fcursor.get(), syncOptions.get()); + } + } catch (NoDocumentException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (DisposedException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (CreationException + | IllegalTypeException + | NotRemoveableException + | PropertyVetoException + | WrappedTargetException ex) { + LOGGER.warn("Could not insert entry", ex); + OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } finally { + UnoUndo.leaveUndoContext(doc); } - // Set the new sections name to 'Child_Section' - xChildNamed.setName(OOBibBase.BIB_SECTION_NAME); - // Access the Child_Section's XTextContent interface and insert it into the document - XTextContent xChildSection = UnoRuntime.queryInterface(XTextContent.class, xChildNamed); - text.insertTextContent(mxDocCursor, xChildSection, false); } - private void clearBibTextSectionContent2() - throws NoSuchElementException, WrappedTargetException, IllegalArgumentException, CreationException { - - // Check if the section exists: - XTextSectionsSupplier supplier = UnoRuntime.queryInterface(XTextSectionsSupplier.class, mxDoc); - if (supplier.getTextSections().hasByName(OOBibBase.BIB_SECTION_NAME)) { - XTextSection section = (XTextSection) ((Any) supplier.getTextSections().getByName(OOBibBase.BIB_SECTION_NAME)) - .getObject(); - // Clear it: - XTextCursor cursor = text.createTextCursorByRange(section.getAnchor()); - cursor.gotoRange(section.getAnchor(), false); - cursor.setString(""); - } else { - createBibTextSection2(atEnd); - } - } + /** + * GUI action "Merge citations" + */ + public void guiActionMergeCitationGroups(List databases, OOBibStyle style) { - private void populateBibTextSection(Map entries, OOBibStyle style) - throws NoSuchElementException, WrappedTargetException, PropertyVetoException, - UnknownPropertyException, UndefinedParagraphFormatException, IllegalArgumentException, CreationException { - XTextSectionsSupplier supplier = UnoRuntime.queryInterface(XTextSectionsSupplier.class, mxDoc); - XTextSection section = (XTextSection) ((Any) supplier.getTextSections().getByName(OOBibBase.BIB_SECTION_NAME)) - .getObject(); - XTextCursor cursor = text.createTextCursorByRange(section.getAnchor()); - OOUtil.insertTextAtCurrentLocation(text, cursor, (String) style.getProperty(OOBibStyle.TITLE), - (String) style.getProperty(OOBibStyle.REFERENCE_HEADER_PARAGRAPH_FORMAT)); - insertFullReferenceAtCursor(cursor, entries, style, - (String) style.getProperty(OOBibStyle.REFERENCE_PARAGRAPH_FORMAT)); - insertBookMark(OOBibBase.BIB_SECTION_END_NAME, cursor); - } + final String errorTitle = Localization.lang("Problem combining cite markers"); - private XTextContent insertBookMark(String name, XTextCursor position) - throws IllegalArgumentException, CreationException { - Object bookmark; - try { - bookmark = mxDocFactory.createInstance("com.sun.star.text.Bookmark"); - } catch (Exception e) { - throw new CreationException(e.getMessage()); + OOResult odoc = getXTextDocument(); + if (testDialog(errorTitle, + odoc.asVoidResult(), + styleIsRequired(style), + databaseIsRequired(databases, OOError::noDataBaseIsOpen))) { + return; } - // name the bookmark - XNamed xNamed = UnoRuntime.queryInterface(XNamed.class, bookmark); - xNamed.setName(name); - // get XTextContent interface - XTextContent xTextContent = UnoRuntime.queryInterface(XTextContent.class, bookmark); - // insert bookmark at the end of the document - // instead of mxDocText.getEnd you could use a text cursor's XTextRange interface or any XTextRange - text.insertTextContent(position, xTextContent, true); - position.collapseToEnd(); - return xTextContent; - } + XTextDocument doc = odoc.get(); - private void insertReferenceMark(String name, String citationText, XTextCursor position, boolean withText, - OOBibStyle style) - throws UnknownPropertyException, WrappedTargetException, - PropertyVetoException, IllegalArgumentException, UndefinedCharacterFormatException, CreationException { - - // Check if there is "page info" stored for this citation. If so, insert it into - // the citation text before inserting the citation: - Optional pageInfo = getCustomProperty(name); - String citText; - if ((pageInfo.isPresent()) && !pageInfo.get().isEmpty()) { - citText = style.insertPageInfo(citationText, pageInfo.get()); - } else { - citText = citationText; - } + OOResult fcursor = getFunctionalTextViewCursor(doc, errorTitle); - Object bookmark; - try { - bookmark = mxDocFactory.createInstance("com.sun.star.text.ReferenceMark"); - } catch (Exception e) { - throw new CreationException(e.getMessage()); + if (testDialog(errorTitle, + fcursor.asVoidResult(), + checkStylesExistInTheDocument(style, doc), + checkIfOpenOfficeIsRecordingChanges(doc))) { + return; } - // Name the reference - XNamed xNamed = UnoRuntime.queryInterface(XNamed.class, bookmark); - xNamed.setName(name); - if (withText) { - position.setString(citText); - XPropertySet xCursorProps = UnoRuntime.queryInterface(XPropertySet.class, position); + try { + UnoUndo.enterUndoContext(doc, "Merge citations"); - // Set language to [None]: - xCursorProps.setPropertyValue("CharLocale", new Locale("zxx", "", "")); - if (style.isFormatCitations()) { - String charStyle = style.getCitationCharacterFormat(); - try { - xCursorProps.setPropertyValue(CHAR_STYLE_NAME, charStyle); - } catch (UnknownPropertyException | PropertyVetoException | IllegalArgumentException | - WrappedTargetException ex) { - throw new UndefinedCharacterFormatException(charStyle); - } + OOFrontend frontend = new OOFrontend(doc); + boolean madeModifications = EditMerge.mergeCitationGroups(doc, frontend, style); + if (madeModifications) { + UnoCrossRef.refresh(doc); + Update.SyncOptions syncOptions = new Update.SyncOptions(databases); + Update.resyncDocument(doc, style, fcursor.get(), syncOptions); } - } else { - position.setString(""); - } + } catch (NoDocumentException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (DisposedException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (CreationException + | IllegalTypeException + | NotRemoveableException + | PropertyVetoException + | WrappedTargetException + | com.sun.star.lang.IllegalArgumentException ex) { + LOGGER.warn("Problem combining cite markers", ex); + OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } finally { + UnoUndo.leaveUndoContext(doc); + fcursor.get().restore(doc); + } + } // MergeCitationGroups - // get XTextContent interface - XTextContent xTextContent = UnoRuntime.queryInterface(XTextContent.class, bookmark); + /** + * GUI action "Separate citations". + *

+ * Do the opposite of MergeCitationGroups. Combined markers are split, with a space inserted between. + */ + public void guiActionSeparateCitations(List databases, OOBibStyle style) { - position.getText().insertTextContent(position, xTextContent, true); + final String errorTitle = Localization.lang("Problem during separating cite markers"); - // Check if we should italicize the "et al." string in citations: - boolean italicize = style.getBooleanCitProperty(OOBibStyle.ITALIC_ET_AL); - if (italicize) { - String etAlString = style.getStringCitProperty(OOBibStyle.ET_AL_STRING); - int index = citText.indexOf(etAlString); - if (index >= 0) { - italicizeOrBold(position, true, index, index + etAlString.length()); - } + OOResult odoc = getXTextDocument(); + if (testDialog(errorTitle, + odoc.asVoidResult(), + styleIsRequired(style), + databaseIsRequired(databases, OOError::noDataBaseIsOpen))) { + return; } - position.collapseToEnd(); - } + XTextDocument doc = odoc.get(); + OOResult fcursor = getFunctionalTextViewCursor(doc, errorTitle); + + if (testDialog(errorTitle, + fcursor.asVoidResult(), + checkStylesExistInTheDocument(style, doc), + checkIfOpenOfficeIsRecordingChanges(doc))) { + return; + } - private void removeReferenceMark(String name) throws NoSuchElementException, WrappedTargetException { - XNameAccess xReferenceMarks = getReferenceMarks(); - if (xReferenceMarks.hasByName(name)) { - Object referenceMark = xReferenceMarks.getByName(name); - XTextContent bookmark = UnoRuntime.queryInterface(XTextContent.class, referenceMark); - text.removeTextContent(bookmark); + try { + UnoUndo.enterUndoContext(doc, "Separate citations"); + + OOFrontend frontend = new OOFrontend(doc); + boolean madeModifications = EditSeparate.separateCitations(doc, frontend, databases, style); + if (madeModifications) { + UnoCrossRef.refresh(doc); + Update.SyncOptions syncOptions = new Update.SyncOptions(databases); + Update.resyncDocument(doc, style, fcursor.get(), syncOptions); + } + } catch (NoDocumentException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (DisposedException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (CreationException + | IllegalTypeException + | NotRemoveableException + | PropertyVetoException + | WrappedTargetException + | com.sun.star.lang.IllegalArgumentException ex) { + LOGGER.warn("Problem during separating cite markers", ex); + OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } finally { + UnoUndo.leaveUndoContext(doc); + fcursor.get().restore(doc); } } /** - * Get the XTextRange corresponding to the named bookmark. + * GUI action for "Export cited" + *

+ * Does not refresh the bibliography. * - * @param name The name of the bookmark to find. - * @return The XTextRange for the bookmark. - * @throws WrappedTargetException - * @throws NoSuchElementException + * @param returnPartialResult If there are some unresolved keys, shall we return an otherwise nonempty result, or Optional.empty()? */ - private XTextRange getBookmarkRange(String name) throws NoSuchElementException, WrappedTargetException { - XNameAccess xNamedBookmarks = getBookmarks(); + public Optional exportCitedHelper(List databases, boolean returnPartialResult) { - // retrieve bookmark by name - if (!xNamedBookmarks.hasByName(name)) { - return null; - } - Object foundBookmark = xNamedBookmarks.getByName(name); - XTextContent xFoundBookmark = UnoRuntime.queryInterface(XTextContent.class, foundBookmark); - return xFoundBookmark.getAnchor(); - } - - private XNameAccess getBookmarks() { - // query XBookmarksSupplier from document model and get bookmarks collection - XBookmarksSupplier xBookmarksSupplier = UnoRuntime.queryInterface(XBookmarksSupplier.class, xCurrentComponent); - XNameAccess xNamedBookmarks = xBookmarksSupplier.getBookmarks(); - return xNamedBookmarks; - } + final Optional FAIL = Optional.empty(); + final String errorTitle = Localization.lang("Unable to generate new library"); - private void italicizeOrBold(XTextCursor position, boolean italicize, int start, int end) - throws UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException { - XTextRange range = position.getStart(); - XTextCursor cursor = position.getText().createTextCursorByRange(range); - cursor.goRight((short) start, false); - cursor.goRight((short) (end - start), true); - XPropertySet xcp = UnoRuntime.queryInterface(XPropertySet.class, cursor); - if (italicize) { - xcp.setPropertyValue("CharPosture", com.sun.star.awt.FontSlant.ITALIC); - } else { - xcp.setPropertyValue("CharWeight", com.sun.star.awt.FontWeight.BOLD); + OOResult odoc = getXTextDocument(); + if (testDialog(errorTitle, + odoc.asVoidResult(), + databaseIsRequired(databases, OOError::noDataBaseIsOpenForExport))) { + return FAIL; } - } + XTextDocument doc = odoc.get(); - public void combineCiteMarkers(List databases, OOBibStyle style) - throws IOException, WrappedTargetException, NoSuchElementException, IllegalArgumentException, - UndefinedCharacterFormatException, UnknownPropertyException, PropertyVetoException, CreationException, - BibEntryNotFoundException { - XNameAccess nameAccess = getReferenceMarks(); - // TODO: doesn't work for citations in footnotes/tables - List names = getSortedReferenceMarks(nameAccess); - - final XTextRangeCompare compare = UnoRuntime.queryInterface(XTextRangeCompare.class, text); - - int piv = 0; - boolean madeModifications = false; - while (piv < (names.size() - 1)) { - XTextRange range1 = UnoRuntime.queryInterface(XTextContent.class, nameAccess.getByName(names.get(piv))) - .getAnchor().getEnd(); - XTextRange range2 = UnoRuntime.queryInterface(XTextContent.class, nameAccess.getByName(names.get(piv + 1))) - .getAnchor().getStart(); - if (range1.getText() != range2.getText()) { - piv++; - continue; - } - XTextCursor mxDocCursor = range1.getText().createTextCursorByRange(range1); - mxDocCursor.goRight((short) 1, true); - boolean couldExpand = true; - while (couldExpand && (compare.compareRegionEnds(mxDocCursor, range2) > 0)) { - couldExpand = mxDocCursor.goRight((short) 1, true); - } - String cursorText = mxDocCursor.getString(); - // Check if the string contains no line breaks and only whitespace: - if ((cursorText.indexOf('\n') == -1) && cursorText.trim().isEmpty()) { - - // If we are supposed to set character format for citations, test this before - // making any changes. This way we can throw an exception before any reference - // marks are removed, preventing damage to the user's document: - if (style.isFormatCitations()) { - XPropertySet xCursorProps = UnoRuntime.queryInterface(XPropertySet.class, mxDocCursor); - String charStyle = style.getCitationCharacterFormat(); - try { - xCursorProps.setPropertyValue(CHAR_STYLE_NAME, charStyle); - } catch (UnknownPropertyException | PropertyVetoException | IllegalArgumentException | - WrappedTargetException ex) { - // Setting the character format failed, so we throw an exception that - // will result in an error message for the user: - throw new UndefinedCharacterFormatException(charStyle); - } - } + try { - List keys = parseRefMarkName(names.get(piv)); - keys.addAll(parseRefMarkName(names.get(piv + 1))); - removeReferenceMark(names.get(piv)); - removeReferenceMark(names.get(piv + 1)); - List entries = new ArrayList<>(); - for (String key : keys) { - for (BibDatabase database : databases) { - Optional entry = database.getEntryByCitationKey(key); - if (entry.isPresent()) { - entries.add(entry.get()); - break; - } - } + ExportCited.GenerateDatabaseResult result; + try { + UnoUndo.enterUndoContext(doc, "Changes during \"Export cited\""); + result = ExportCited.generateDatabase(doc, databases); + } finally { + // There should be no changes, thus no Undo entry should appear + // in LibreOffice. + UnoUndo.leaveUndoContext(doc); + } + + if (!result.newDatabase.hasEntries()) { + dialogService.showErrorDialogAndWait( + Localization.lang("Unable to generate new library"), + Localization.lang("Your OpenOffice/LibreOffice document references" + + " no citation keys" + + " which could also be found in your current library.")); + return FAIL; + } + + List unresolvedKeys = result.unresolvedKeys; + if (!unresolvedKeys.isEmpty()) { + dialogService.showErrorDialogAndWait( + Localization.lang("Unable to generate new library"), + Localization.lang("Your OpenOffice/LibreOffice document references" + + " at least %0 citation keys" + + " which could not be found in your current library." + + " Some of these are %1.", + String.valueOf(unresolvedKeys.size()), + String.join(", ", unresolvedKeys))); + if (returnPartialResult) { + return Optional.of(result.newDatabase); + } else { + return FAIL; } - Collections.sort(entries, new FieldComparator(StandardField.YEAR)); - String keyString = String.join(",", entries.stream().map(entry -> entry.getCitationKey().orElse("")) - .collect(Collectors.toList())); - // Insert bookmark: - String bName = getUniqueReferenceMarkName(keyString, OOBibBase.AUTHORYEAR_PAR); - insertReferenceMark(bName, "tmp", mxDocCursor, true, style); - names.set(piv + 1, bName); - madeModifications = true; } - piv++; - } - if (madeModifications) { - updateSortedReferenceMarks(); - refreshCiteMarkers(databases, style); + return Optional.of(result.newDatabase); + } catch (NoDocumentException ex) { + OOError.from(ex).showErrorDialog(dialogService); + } catch (DisposedException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (WrappedTargetException + | com.sun.star.lang.IllegalArgumentException ex) { + LOGGER.warn("Problem generating new database.", ex); + OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); } + return FAIL; } /** - * Do the opposite of combineCiteMarkers. - * Combined markers are split, with a space inserted between. + * GUI action, refreshes citation markers and bibliography. + * + * @param databases Must have at least one. + * @param style Style. */ - public void unCombineCiteMarkers(List databases, OOBibStyle style) - throws IOException, WrappedTargetException, NoSuchElementException, IllegalArgumentException, - UndefinedCharacterFormatException, UnknownPropertyException, PropertyVetoException, CreationException, - BibEntryNotFoundException { - XNameAccess nameAccess = getReferenceMarks(); - List names = getSortedReferenceMarks(nameAccess); - - final XTextRangeCompare compare = UnoRuntime.queryInterface(XTextRangeCompare.class, text); - - int pivot = 0; - boolean madeModifications = false; - while (pivot < (names.size())) { - XTextRange range1 = UnoRuntime.queryInterface(XTextContent.class, nameAccess.getByName(names.get(pivot))) - .getAnchor(); - - XTextCursor textCursor = range1.getText().createTextCursorByRange(range1); - - // If we are supposed to set character format for citations, test this before - // making any changes. This way we can throw an exception before any reference - // marks are removed, preventing damage to the user's document: - if (style.isFormatCitations()) { - XPropertySet xCursorProps = UnoRuntime.queryInterface(XPropertySet.class, textCursor); - String charStyle = style.getCitationCharacterFormat(); - try { - xCursorProps.setPropertyValue(CHAR_STYLE_NAME, charStyle); - } catch (UnknownPropertyException | PropertyVetoException | IllegalArgumentException | - WrappedTargetException ex) { - // Setting the character format failed, so we throw an exception that - // will result in an error message for the user: - throw new UndefinedCharacterFormatException(charStyle); - } - } + public void guiActionUpdateDocument(List databases, OOBibStyle style) { - List keys = parseRefMarkName(names.get(pivot)); - if (keys.size() > 1) { - removeReferenceMark(names.get(pivot)); - - // Insert bookmark for each key - int last = keys.size() - 1; - int i = 0; - for (String key : keys) { - String newName = getUniqueReferenceMarkName(key, OOBibBase.AUTHORYEAR_PAR); - insertReferenceMark(newName, "tmp", textCursor, true, style); - textCursor.collapseToEnd(); - if (i != last) { - textCursor.setString(" "); - textCursor.collapseToEnd(); - } - i++; - } - madeModifications = true; - } - pivot++; - } - if (madeModifications) { - updateSortedReferenceMarks(); - refreshCiteMarkers(databases, style); - } - } + final String errorTitle = Localization.lang("Unable to synchronize bibliography"); - public BibDatabase generateDatabase(List databases) - throws NoSuchElementException, WrappedTargetException { - BibDatabase resultDatabase = new BibDatabase(); - List cited = findCitedKeys(); - List entriesToInsert = new ArrayList<>(); - - // For each cited key - for (String key : cited) { - // Loop through the available databases - for (BibDatabase loopDatabase : databases) { - Optional entry = loopDatabase.getEntryByCitationKey(key); - // If entry found - if (entry.isPresent()) { - BibEntry clonedEntry = (BibEntry) entry.get().clone(); - // Insert a copy of the entry - entriesToInsert.add(clonedEntry); - // Check if the cloned entry has a crossref field - clonedEntry.getField(StandardField.CROSSREF).ifPresent(crossref -> { - // If the crossref entry is not already in the database - if (!resultDatabase.getEntryByCitationKey(crossref).isPresent()) { - // Add it if it is in the current library - loopDatabase.getEntryByCitationKey(crossref).ifPresent(entriesToInsert::add); - } - }); - - // Be happy with the first found BibEntry and move on to next key - break; - } - } - } - resultDatabase.insertEntries(entriesToInsert); - return resultDatabase; - } + try { - private static class ComparableMark implements Comparable { + OOResult odoc = getXTextDocument(); + if (testDialog(errorTitle, + odoc.asVoidResult(), + styleIsRequired(style))) { + return; + } - private final String name; - private final Point position; + XTextDocument doc = odoc.get(); - public ComparableMark(String name, Point position) { - this.name = name; - this.position = position; - } + OOResult fcursor = getFunctionalTextViewCursor(doc, errorTitle); - @Override - public int compareTo(ComparableMark other) { - if (position.Y == other.position.Y) { - return position.X - other.position.X; - } else { - return position.Y - other.position.Y; - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; + if (testDialog(errorTitle, + fcursor.asVoidResult(), + checkStylesExistInTheDocument(style, doc), + checkIfOpenOfficeIsRecordingChanges(doc))) { + return; } - if (o instanceof ComparableMark) { - ComparableMark other = (ComparableMark) o; - return (this.position.X == other.position.X) && (this.position.Y == other.position.Y) - && Objects.equals(this.name, other.name); + OOFrontend frontend = new OOFrontend(doc); + if (testDialog(errorTitle, checkRangeOverlaps(doc, frontend))) { + return; } - return false; - } - - public String getName() { - return name; - } - - @Override - public int hashCode() { - return Objects.hash(position, name); - } - } - - private class DocumentTitleViewModel { - private final XTextDocument xTextDocument; - private final String description; + List unresolvedKeys; + try { + UnoUndo.enterUndoContext(doc, "Refresh bibliography"); - public DocumentTitleViewModel(XTextDocument xTextDocument) { - this.xTextDocument = xTextDocument; - this.description = getDocumentTitle(xTextDocument).orElse(""); - } + Update.SyncOptions syncOptions = new Update.SyncOptions(databases); + syncOptions + .setUpdateBibliography(true) + .setAlwaysAddCitedOnPages(this.alwaysAddCitedOnPages); - public XTextDocument getXtextDocument() { - return xTextDocument; - } + unresolvedKeys = Update.synchronizeDocument(doc, frontend, style, fcursor.get(), syncOptions); + } finally { + UnoUndo.leaveUndoContext(doc); + fcursor.get().restore(doc); + } - @Override - public String toString() { - return description; + if (!unresolvedKeys.isEmpty()) { + String msg = Localization.lang( + "Your OpenOffice/LibreOffice document references the citation key '%0'," + + " which could not be found in your current library.", + unresolvedKeys.get(0)); + dialogService.showErrorDialogAndWait(errorTitle, msg); + } + } catch (NoDocumentException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (DisposedException ex) { + OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); + } catch (CreationException + | WrappedTargetException + | com.sun.star.lang.IllegalArgumentException ex) { + LOGGER.warn("Could not update bibliography", ex); + OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); } } } diff --git a/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java b/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java new file mode 100644 index 00000000000..24660410fc3 --- /dev/null +++ b/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java @@ -0,0 +1,236 @@ +package org.jabref.gui.openoffice; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.jabref.gui.DialogService; +import org.jabref.logic.l10n.Localization; +import org.jabref.logic.openoffice.NoDocumentFoundException; +import org.jabref.model.openoffice.uno.CreationException; +import org.jabref.model.openoffice.uno.NoDocumentException; +import org.jabref.model.openoffice.uno.UnoCast; +import org.jabref.model.openoffice.uno.UnoTextDocument; +import org.jabref.model.openoffice.util.OOResult; + +import com.sun.star.comp.helper.BootstrapException; +import com.sun.star.container.NoSuchElementException; +import com.sun.star.container.XEnumeration; +import com.sun.star.container.XEnumerationAccess; +import com.sun.star.frame.XDesktop; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.text.XTextDocument; +import com.sun.star.uno.XComponentContext; + +/** + * Establish connection to a document opened in OpenOffice or LibreOffice. + */ +class OOBibBaseConnect { + + private final DialogService dialogService; + private final XDesktop xDesktop; + + /** + * Created when connected to a document. + *

+ * Cleared (to null) when we discover we lost the connection. + */ + private XTextDocument xTextDocument; + + public OOBibBaseConnect(Path loPath, DialogService dialogService) + throws + BootstrapException, + CreationException { + + this.dialogService = dialogService; + this.xDesktop = simpleBootstrap(loPath); + } + + private XDesktop simpleBootstrap(Path loPath) + throws + CreationException, + BootstrapException { + + // Get the office component context: + XComponentContext context = org.jabref.gui.openoffice.Bootstrap.bootstrap(loPath); + XMultiComponentFactory sem = context.getServiceManager(); + + // Create the desktop, which is the root frame of the + // hierarchy of frames that contain viewable components: + Object desktop; + try { + desktop = sem.createInstanceWithContext("com.sun.star.frame.Desktop", context); + } catch (com.sun.star.uno.Exception e) { + throw new CreationException(e.getMessage()); + } + return UnoCast.cast(XDesktop.class, desktop).get(); + } + + private static List getTextDocuments(XDesktop desktop) + throws + NoSuchElementException, + WrappedTargetException { + + List result = new ArrayList<>(); + + XEnumerationAccess enumAccess = desktop.getComponents(); + XEnumeration compEnum = enumAccess.createEnumeration(); + + while (compEnum.hasMoreElements()) { + Object next = compEnum.nextElement(); + XComponent comp = UnoCast.cast(XComponent.class, next).get(); + Optional doc = UnoCast.cast(XTextDocument.class, comp); + doc.ifPresent(result::add); + } + return result; + } + + /** + * Run a dialog allowing the user to choose among the documents in `list`. + * + * @return Null if no document was selected. Otherwise the document selected. + */ + private static XTextDocument selectDocumentDialog(List list, + DialogService dialogService) { + + class DocumentTitleViewModel { + + private final XTextDocument xTextDocument; + private final String description; + + public DocumentTitleViewModel(XTextDocument xTextDocument) { + this.xTextDocument = xTextDocument; + this.description = UnoTextDocument.getFrameTitle(xTextDocument).orElse(""); + } + + public XTextDocument getXtextDocument() { + return xTextDocument; + } + + @Override + public String toString() { + return description; + } + } + + List viewModel = (list.stream() + .map(DocumentTitleViewModel::new) + .collect(Collectors.toList())); + + // This whole method is part of a background task when + // auto-detecting instances, so we need to show dialog in FX + // thread + Optional selectedDocument = + (dialogService + .showChoiceDialogAndWait(Localization.lang("Select document"), + Localization.lang("Found documents:"), + Localization.lang("Use selected document"), + viewModel)); + + return (selectedDocument + .map(DocumentTitleViewModel::getXtextDocument) + .orElse(null)); + } + + /** + * Choose a document to work with. + *

+ * Assumes we have already connected to LibreOffice or OpenOffice. + *

+ * If there is a single document to choose from, selects that. If there are more than one, shows selection dialog. If there are none, throws NoDocumentFoundException + *

+ * After successful selection connects to the selected document and extracts some frequently used parts (starting points for managing its content). + *

+ * Finally initializes this.xTextDocument with the selected document and parts extracted. + */ + public void selectDocument(boolean autoSelectForSingle) + throws + NoDocumentFoundException, + NoSuchElementException, + WrappedTargetException { + + XTextDocument selected; + List textDocumentList = getTextDocuments(this.xDesktop); + if (textDocumentList.isEmpty()) { + throw new NoDocumentFoundException("No Writer documents found"); + } else if (textDocumentList.size() == 1 && autoSelectForSingle) { + selected = textDocumentList.get(0); // Get the only one + } else { // Bring up a dialog + selected = OOBibBaseConnect.selectDocumentDialog(textDocumentList, + this.dialogService); + } + + if (selected == null) { + return; + } + + this.xTextDocument = selected; + } + + /** + * Mark the current document as missing. + */ + private void forgetDocument() { + this.xTextDocument = null; + } + + /** + * A simple test for document availability. + *

+ * See also `isDocumentConnectionMissing` for a test actually attempting to use teh connection. + */ + public boolean isConnectedToDocument() { + return this.xTextDocument != null; + } + + /** + * @return true if we are connected to a document + */ + public boolean isDocumentConnectionMissing() { + XTextDocument doc = this.xTextDocument; + + if (doc == null) { + return true; + } + + if (UnoTextDocument.isDocumentConnectionMissing(doc)) { + forgetDocument(); + return true; + } + return false; + } + + /** + * Either return a valid XTextDocument or throw NoDocumentException. + */ + public XTextDocument getXTextDocumentOrThrow() + throws + NoDocumentException { + if (isDocumentConnectionMissing()) { + throw new NoDocumentException("Not connected to document"); + } + return this.xTextDocument; + } + + public OOResult getXTextDocument() { + if (isDocumentConnectionMissing()) { + return OOResult.error(OOError.from(new NoDocumentException())); + } + return OOResult.ok(this.xTextDocument); + } + + /** + * The title of the current document, or Optional.empty() + */ + public Optional getCurrentDocumentTitle() { + if (isDocumentConnectionMissing()) { + return Optional.empty(); + } else { + return UnoTextDocument.getFrameTitle(this.xTextDocument); + } + } +} // end of OOBibBaseConnect diff --git a/src/main/java/org/jabref/gui/openoffice/OOError.java b/src/main/java/org/jabref/gui/openoffice/OOError.java new file mode 100644 index 00000000000..c8ad7ef53aa --- /dev/null +++ b/src/main/java/org/jabref/gui/openoffice/OOError.java @@ -0,0 +1,141 @@ +package org.jabref.gui.openoffice; + +import org.jabref.gui.DialogService; +import org.jabref.logic.JabRefException; +import org.jabref.logic.l10n.Localization; +import org.jabref.logic.openoffice.NoDocumentFoundException; +import org.jabref.model.openoffice.uno.NoDocumentException; + +import com.sun.star.lang.DisposedException; + +class OOError extends JabRefException { + + private String localizedTitle; + + public OOError(String title, String localizedMessage) { + super(localizedMessage, localizedMessage); + this.localizedTitle = title; + } + + public OOError(String title, String localizedMessage, Throwable cause) { + super(localizedMessage, localizedMessage, cause); + this.localizedTitle = title; + } + + public String getTitle() { + return localizedTitle; + } + + public OOError setTitle(String title) { + localizedTitle = title; + return this; + } + + public void showErrorDialog(DialogService dialogService) { + dialogService.showErrorDialogAndWait(getTitle(), getLocalizedMessage()); + } + + /* + * Conversions from exception caught + */ + + public static OOError from(JabRefException err) { + return new OOError( + Localization.lang("JabRefException"), + err.getLocalizedMessage(), + err); + } + + // For DisposedException + public static OOError from(DisposedException err) { + return new OOError( + Localization.lang("Connection lost"), + Localization.lang("Connection to OpenOffice/LibreOffice has been lost." + + " Please make sure OpenOffice/LibreOffice is running," + + " and try to reconnect."), + err); + } + + // For NoDocumentException + public static OOError from(NoDocumentException err) { + return new OOError( + Localization.lang("Not connected to document"), + Localization.lang("Not connected to any Writer document." + + " Please make sure a document is open," + + " and use the 'Select Writer document' button" + + " to connect to it."), + err); + } + + // For NoDocumentFoundException + public static OOError from(NoDocumentFoundException err) { + return new OOError( + Localization.lang("No Writer documents found"), + Localization.lang("Could not connect to any Writer document." + + " Please make sure a document is open" + + " before using the 'Select Writer document' button" + + " to connect to it."), + err); + } + + public static OOError fromMisc(Exception err) { + return new OOError( + "Exception", + err.getMessage(), + err); + } + + /* + * Messages for error dialog. These are not thrown. + */ + + // noDataBaseIsOpenForCiting + public static OOError noDataBaseIsOpenForCiting() { + return new OOError( + Localization.lang("No database"), + Localization.lang("No bibliography database is open for citation.") + + "\n" + + Localization.lang("Open one before citing.")); + } + + public static OOError noDataBaseIsOpenForSyncingAfterCitation() { + return new OOError( + Localization.lang("No database"), + Localization.lang("No database is open for updating citation markers after citing.") + + "\n" + + Localization.lang("Open one before citing.")); + } + + // noDataBaseIsOpenForExport + public static OOError noDataBaseIsOpenForExport() { + return new OOError( + Localization.lang("No database is open"), + Localization.lang("We need a database to export from. Open one.")); + } + + // noDataBaseIsOpenForExport + public static OOError noDataBaseIsOpen() { + return new OOError( + Localization.lang("No database is open"), + Localization.lang("This operation requires a bibliography database.")); + } + + // noValidStyleSelected + public static OOError noValidStyleSelected() { + return new OOError(Localization.lang("No valid style file defined"), + Localization.lang("No bibliography style is selected for citation.") + + "\n" + + Localization.lang("Select one before citing.") + + "\n" + + Localization.lang("You must select either a valid style file," + + " or use one of the default styles.")); + } + + // noEntriesSelectedForCitation + public static OOError noEntriesSelectedForCitation() { + return new OOError(Localization.lang("No entries selected for citation"), + Localization.lang("No bibliography entries are selected for citation.") + + "\n" + + Localization.lang("Select some before citing.")); + } +} diff --git a/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java b/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java index 662a4aac69e..8c40775ce79 100644 --- a/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java +++ b/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java @@ -1,8 +1,6 @@ package org.jabref.gui.openoffice; -import java.io.FileNotFoundException; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -48,24 +46,17 @@ import org.jabref.logic.l10n.Localization; import org.jabref.logic.openoffice.OpenOfficeFileSearch; import org.jabref.logic.openoffice.OpenOfficePreferences; -import org.jabref.logic.openoffice.UndefinedParagraphFormatException; +import org.jabref.logic.openoffice.action.Update; import org.jabref.logic.openoffice.style.OOBibStyle; import org.jabref.logic.openoffice.style.StyleLoader; import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; +import org.jabref.model.openoffice.style.CitationType; import org.jabref.model.openoffice.uno.CreationException; -import org.jabref.model.openoffice.uno.NoDocumentException; import org.jabref.preferences.PreferencesService; -import com.sun.star.beans.IllegalTypeException; -import com.sun.star.beans.NotRemoveableException; -import com.sun.star.beans.PropertyExistException; -import com.sun.star.beans.PropertyVetoException; -import com.sun.star.beans.UnknownPropertyException; import com.sun.star.comp.helper.BootstrapException; -import com.sun.star.container.NoSuchElementException; -import com.sun.star.lang.WrappedTargetException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,19 +90,19 @@ public class OpenOfficePanel { private final UndoManager undoManager; private final TaskExecutor taskExecutor; private final StyleLoader loader; - private OpenOfficePreferences ooPrefs; + private OpenOfficePreferences openOfficePreferences; private OOBibBase ooBase; private OOBibStyle style; public OpenOfficePanel(PreferencesService preferencesService, - OpenOfficePreferences ooPrefs, + OpenOfficePreferences openOfficePreferences, KeyBindingRepository keyBindingRepository, TaskExecutor taskExecutor, DialogService dialogService, StateManager stateManager, UndoManager undoManager) { ActionFactory factory = new ActionFactory(keyBindingRepository); - this.ooPrefs = ooPrefs; + this.openOfficePreferences = openOfficePreferences; this.preferencesService = preferencesService; this.taskExecutor = taskExecutor; this.dialogService = dialogService; @@ -141,7 +132,7 @@ public OpenOfficePanel(PreferencesService preferencesService, update.setTooltip(new Tooltip(Localization.lang("Sync OpenOffice/LibreOffice bibliography"))); update.setMaxWidth(Double.MAX_VALUE); - loader = new StyleLoader(ooPrefs, + loader = new StyleLoader(openOfficePreferences, preferencesService.getLayoutFormatterPreferences(Globals.journalAbbreviationRepository)); initPanel(); @@ -151,24 +142,38 @@ public Node getContent() { return vbox; } + /* Note: the style may still be null on return. + * + * Return true if failed. In this case the dialog is already shown. + */ + private boolean getOrUpdateTheStyle(String title) { + final boolean FAIL = true; + final boolean PASS = false; + + if (style == null) { + style = loader.getUsedStyle(); + } else { + try { + style.ensureUpToDate(); + } catch (IOException ex) { + LOGGER.warn("Unable to reload style file '" + style.getPath() + "'", ex); + String msg = (Localization.lang("Unable to reload style file") + + "'" + style.getPath() + "'" + + "\n" + ex.getMessage()); + new OOError(title, msg, ex).showErrorDialog(dialogService); + return FAIL; + } + } + return PASS; + } + private void initPanel() { connect.setOnAction(e -> connectAutomatically()); manualConnect.setOnAction(e -> connectManually()); selectDocument.setTooltip(new Tooltip(Localization.lang("Select which open Writer document to work on"))); - selectDocument.setOnAction(e -> { - - try { - ooBase.selectDocument(); - dialogService.notify(Localization.lang("Connected to document") + ": " - + ooBase.getCurrentDocumentTitle().orElse("")); - } catch (WrappedTargetException | IndexOutOfBoundsException | - NoSuchElementException | NoDocumentException ex) { - LOGGER.warn("Problem connecting", ex); - dialogService.showErrorDialogAndWait(ex); - } - }); + selectDocument.setOnAction(e -> ooBase.guiActionSelectDocument(false)); setStyleFile.setMaxWidth(Double.MAX_VALUE); setStyleFile.setOnAction(event -> @@ -184,107 +189,53 @@ private void initPanel() { })); pushEntries.setTooltip(new Tooltip(Localization.lang("Cite selected entries between parenthesis"))); - pushEntries.setOnAction(e -> pushEntries(true, true, false)); + pushEntries.setOnAction(e -> pushEntries(CitationType.AUTHORYEAR_PAR, false)); pushEntries.setMaxWidth(Double.MAX_VALUE); pushEntriesInt.setTooltip(new Tooltip(Localization.lang("Cite selected entries with in-text citation"))); - pushEntriesInt.setOnAction(e -> pushEntries(false, true, false)); + pushEntriesInt.setOnAction(e -> pushEntries(CitationType.AUTHORYEAR_INTEXT, false)); pushEntriesInt.setMaxWidth(Double.MAX_VALUE); pushEntriesEmpty.setTooltip(new Tooltip(Localization.lang("Insert a citation without text (the entry will appear in the reference list)"))); - pushEntriesEmpty.setOnAction(e -> pushEntries(false, false, false)); + pushEntriesEmpty.setOnAction(e -> pushEntries(CitationType.INVISIBLE_CIT, false)); pushEntriesEmpty.setMaxWidth(Double.MAX_VALUE); pushEntriesAdvanced.setTooltip(new Tooltip(Localization.lang("Cite selected entries with extra information"))); - pushEntriesAdvanced.setOnAction(e -> pushEntries(false, true, true)); + pushEntriesAdvanced.setOnAction(e -> pushEntries(CitationType.AUTHORYEAR_INTEXT, true)); pushEntriesAdvanced.setMaxWidth(Double.MAX_VALUE); update.setTooltip(new Tooltip(Localization.lang("Ensure that the bibliography is up-to-date"))); update.setOnAction(event -> { - try { - if (style == null) { - style = loader.getUsedStyle(); - } else { - style.ensureUpToDate(); - } - - ooBase.updateSortedReferenceMarks(); - - List databases = getBaseList(); - List unresolvedKeys = ooBase.refreshCiteMarkers(databases, style); - ooBase.rebuildBibTextSection(databases, style); - if (!unresolvedKeys.isEmpty()) { - dialogService.showErrorDialogAndWait(Localization.lang("Unable to synchronize bibliography"), - Localization.lang("Your OpenOffice/LibreOffice document references the citation key '%0', which could not be found in your current library.", - unresolvedKeys.get(0))); - } - } catch (UndefinedCharacterFormatException ex) { - reportUndefinedCharacterFormat(ex); - } catch (UndefinedParagraphFormatException ex) { - reportUndefinedParagraphFormat(ex); - } catch (ConnectionLostException ex) { - showConnectionLostErrorMessage(); - } catch (IOException ex) { - LOGGER.warn("Problem with style file", ex); - dialogService.showErrorDialogAndWait(Localization.lang("No valid style file defined"), - Localization.lang("You must select either a valid style file, or use one of the default styles.")); - } catch (BibEntryNotFoundException ex) { - LOGGER.debug("BibEntry not found", ex); - dialogService.showErrorDialogAndWait(Localization.lang("Unable to synchronize bibliography"), Localization.lang( - "Your OpenOffice/LibreOffice document references the citation key '%0', which could not be found in your current library.", - ex.getCitationKey())); - } catch (com.sun.star.lang.IllegalArgumentException | PropertyVetoException | UnknownPropertyException | WrappedTargetException | NoSuchElementException | - CreationException ex) { - LOGGER.warn("Could not update bibliography", ex); + String title = Localization.lang("Could not update bibliography"); + if (getOrUpdateTheStyle(title)) { + return; } + List databases = getBaseList(); + ooBase.guiActionUpdateDocument(databases, style); }); merge.setMaxWidth(Double.MAX_VALUE); merge.setTooltip(new Tooltip(Localization.lang("Combine pairs of citations that are separated by spaces only"))); - merge.setOnAction(e -> { - try { - ooBase.combineCiteMarkers(getBaseList(), style); - } catch (UndefinedCharacterFormatException ex) { - reportUndefinedCharacterFormat(ex); - } catch (com.sun.star.lang.IllegalArgumentException | UnknownPropertyException | PropertyVetoException | - CreationException | NoSuchElementException | WrappedTargetException | IOException | - BibEntryNotFoundException ex) { - LOGGER.warn("Problem combining cite markers", ex); - } - }); + merge.setOnAction(e -> ooBase.guiActionMergeCitationGroups(getBaseList(), style)); unmerge.setMaxWidth(Double.MAX_VALUE); unmerge.setTooltip(new Tooltip(Localization.lang("Separate merged citations"))); - unmerge.setOnAction(e -> { - try { - ooBase.unCombineCiteMarkers(getBaseList(), style); - } catch (UndefinedCharacterFormatException ex) { - reportUndefinedCharacterFormat(ex); - } catch (com.sun.star.lang.IllegalArgumentException | UnknownPropertyException | PropertyVetoException | - CreationException | NoSuchElementException | WrappedTargetException | IOException | - BibEntryNotFoundException ex) { - LOGGER.warn("Problem uncombining cite markers", ex); - } - }); + unmerge.setOnAction(e -> ooBase.guiActionSeparateCitations(getBaseList(), style)); ContextMenu settingsMenu = createSettingsPopup(); settingsB.setMaxWidth(Double.MAX_VALUE); settingsB.setContextMenu(settingsMenu); settingsB.setOnAction(e -> settingsMenu.show(settingsB, Side.BOTTOM, 0, 0)); manageCitations.setMaxWidth(Double.MAX_VALUE); - manageCitations.setOnAction(e -> dialogService.showCustomDialogAndWait(new ManageCitationsDialogView(ooBase))); + manageCitations.setOnAction(e -> { + ManageCitationsDialogView dialog = new ManageCitationsDialogView(ooBase); + if (dialog.isOkToShowThisDialog()) { + dialogService.showCustomDialogAndWait(dialog); + } + }); exportCitations.setMaxWidth(Double.MAX_VALUE); exportCitations.setOnAction(event -> exportEntries()); - selectDocument.setDisable(true); - pushEntries.setDisable(true); - pushEntriesInt.setDisable(true); - pushEntriesEmpty.setDisable(true); - pushEntriesAdvanced.setDisable(true); - update.setDisable(true); - merge.setDisable(true); - unmerge.setDisable(true); - manageCitations.setDisable(true); - exportCitations.setDisable(true); + updateButtonAvailability(); HBox hbox = new HBox(); hbox.getChildren().addAll(connect, manualConnect, selectDocument, update, help); @@ -304,42 +255,18 @@ private void initPanel() { } private void exportEntries() { - try { - if (style == null) { - style = loader.getUsedStyle(); - } else { - style.ensureUpToDate(); - } - - ooBase.updateSortedReferenceMarks(); - - List databases = getBaseList(); - List unresolvedKeys = ooBase.refreshCiteMarkers(databases, style); - BibDatabase newDatabase = ooBase.generateDatabase(databases); - if (!unresolvedKeys.isEmpty()) { - dialogService.showErrorDialogAndWait(Localization.lang("Unable to generate new library"), - Localization.lang("Your OpenOffice/LibreOffice document references the citation key '%0', which could not be found in your current library.", - unresolvedKeys.get(0))); - } - - BibDatabaseContext databaseContext = new BibDatabaseContext(newDatabase); - + List databases = getBaseList(); + boolean returnPartialResult = false; + Optional newDatabase = ooBase.exportCitedHelper(databases, returnPartialResult); + if (newDatabase.isPresent()) { + BibDatabaseContext databaseContext = new BibDatabaseContext(newDatabase.get()); JabRefGUI.getMainFrame().addTab(databaseContext, true); - } catch (BibEntryNotFoundException ex) { - LOGGER.debug("BibEntry not found", ex); - dialogService.showErrorDialogAndWait(Localization.lang("Unable to synchronize bibliography"), - Localization.lang("Your OpenOffice/LibreOffice document references the citation key '%0', which could not be found in your current library.", - ex.getCitationKey())); - } catch (com.sun.star.lang.IllegalArgumentException | UnknownPropertyException | PropertyVetoException | - UndefinedCharacterFormatException | NoSuchElementException | WrappedTargetException | IOException | - CreationException e) { - LOGGER.warn("Problem generating new database.", e); } } private List getBaseList() { List databases = new ArrayList<>(); - if (ooPrefs.getUseAllDatabases()) { + if (openOfficePreferences.getUseAllDatabases()) { for (BibDatabaseContext database : stateManager.getOpenDatabases()) { databases.add(database.getDatabase()); } @@ -400,7 +327,6 @@ private void connectManually() { connect(); } else { dialogService.showErrorDialogAndWait(Localization.lang("Could not connect to running OpenOffice/LibreOffice."), Localization.lang("If connecting manually, please verify program and library paths.")); - } }) .executeWith(taskExecutor); @@ -409,8 +335,34 @@ private void connectManually() { } } + private void updateButtonAvailability() { + boolean isConnected = (ooBase != null); + boolean isConnectedToDocument = isConnected && !ooBase.isDocumentConnectionMissing(); + + // For these, we need to watch something + boolean hasStyle = true; // (style != null); + boolean hasDatabase = true; // !getBaseList().isEmpty(); + boolean hasSelectedBibEntry = true; + + selectDocument.setDisable(!(isConnected)); + pushEntries.setDisable(!(isConnectedToDocument && hasStyle && hasDatabase)); + + boolean canCite = isConnectedToDocument && hasStyle && hasSelectedBibEntry; + pushEntriesInt.setDisable(!canCite); + pushEntriesEmpty.setDisable(!canCite); + pushEntriesAdvanced.setDisable(!canCite); + + boolean canRefreshDocument = isConnectedToDocument && hasStyle; + update.setDisable(!canRefreshDocument); + merge.setDisable(!canRefreshDocument); + unmerge.setDisable(!canRefreshDocument); + manageCitations.setDisable(!canRefreshDocument); + + exportCitations.setDisable(!(isConnectedToDocument && hasDatabase)); + } + private void connect() { - ooPrefs = preferencesService.getOpenOfficePreferences(); + openOfficePreferences = preferencesService.getOpenOfficePreferences(); Task connectTask = new Task<>() { @@ -418,7 +370,7 @@ private void connect() { protected OOBibBase call() throws Exception { updateProgress(ProgressBar.INDETERMINATE_PROGRESS, ProgressBar.INDETERMINATE_PROGRESS); - var path = Path.of(ooPrefs.getExecutablePath()); + var path = Path.of(openOfficePreferences.getExecutablePath()); return createBibBase(path); } }; @@ -426,45 +378,28 @@ protected OOBibBase call() throws Exception { connectTask.setOnSucceeded(value -> { ooBase = connectTask.getValue(); - try { - ooBase.selectDocument(); - } catch (NoSuchElementException | WrappedTargetException | NoDocumentException ex) { - dialogService.showErrorDialogAndWait(Localization.lang("Error connecting to Writer document"), Localization.lang("You need to open Writer with a document before connecting"), ex); - LOGGER.error("Error connecting to writer document", ex); - } - - if (ooBase.isConnectedToDocument()) { - dialogService.notify(Localization.lang("Connected to document") + ": " + ooBase.getCurrentDocumentTitle().orElse("")); - } + ooBase.guiActionSelectDocument(true); // Enable actions that depend on Connect: - selectDocument.setDisable(false); - pushEntries.setDisable(false); - pushEntriesInt.setDisable(false); - pushEntriesEmpty.setDisable(false); - pushEntriesAdvanced.setDisable(false); - update.setDisable(false); - merge.setDisable(false); - unmerge.setDisable(false); - manageCitations.setDisable(false); - exportCitations.setDisable(false); + updateButtonAvailability(); }); + connectTask.setOnFailed(value -> { Throwable ex = connectTask.getException(); if (ex instanceof UnsatisfiedLinkError) { LOGGER.warn("Could not connect to running OpenOffice/LibreOffice", ex); dialogService.showErrorDialogAndWait(Localization.lang("Unable to connect. One possible reason is that JabRef " - + "and OpenOffice/LibreOffice are not both running in either 32 bit mode or 64 bit mode.")); + + "and OpenOffice/LibreOffice are not both running in either 32 bit mode or 64 bit mode.")); } else if (ex instanceof IOException) { LOGGER.warn("Could not connect to running OpenOffice/LibreOffice", ex); dialogService.showErrorDialogAndWait(Localization.lang("Could not connect to running OpenOffice/LibreOffice."), - Localization.lang("Could not connect to running OpenOffice/LibreOffice.") - + "\n" - + Localization.lang("Make sure you have installed OpenOffice/LibreOffice with Java support.") + "\n" - + Localization.lang("If connecting manually, please verify program and library paths.") + "\n" + "\n" + Localization.lang("Error message:"), - ex); + Localization.lang("Could not connect to running OpenOffice/LibreOffice.") + + "\n" + + Localization.lang("Make sure you have installed OpenOffice/LibreOffice with Java support.") + "\n" + + Localization.lang("If connecting manually, please verify program and library paths.") + "\n" + "\n" + Localization.lang("Error message:"), + ex); } else { dialogService.showErrorDialogAndWait(Localization.lang("Autodetection failed"), Localization.lang("Autodetection failed"), ex); } @@ -474,20 +409,59 @@ protected OOBibBase call() throws Exception { taskExecutor.execute(connectTask); } - private OOBibBase createBibBase(Path loPath) throws IOException, InvocationTargetException, IllegalAccessException, - BootstrapException, CreationException, ClassNotFoundException { - return new OOBibBase(loPath, true, dialogService); + private OOBibBase createBibBase(Path loPath) throws BootstrapException, CreationException { + return new OOBibBase(loPath, dialogService); + } + + /** + * Given the withText and inParenthesis options, return the corresponding citationType. + * + * @param withText False means invisible citation (no text). + * @param inParenthesis True means "(Au and Thor 2000)". False means "Au and Thor (2000)". + */ + private static CitationType citationTypeFromOptions(boolean withText, boolean inParenthesis) { + if (!withText) { + return CitationType.INVISIBLE_CIT; + } + return (inParenthesis + ? CitationType.AUTHORYEAR_PAR + : CitationType.AUTHORYEAR_INTEXT); } - private void pushEntries(boolean inParenthesisIn, boolean withText, boolean addPageInfo) { - if (!ooBase.isConnectedToDocument()) { - dialogService.showErrorDialogAndWait(Localization.lang("Error pushing entries"), Localization.lang("Not connected to any Writer document. Please" + " make sure a document is open, and use the 'Select Writer document' button to connect to it.")); + private void pushEntries(CitationType citationType, boolean addPageInfo) { + final String errorDialogTitle = Localization.lang("Error pushing entries"); + + if (stateManager.getActiveDatabase().isEmpty() + || (stateManager.getActiveDatabase().get().getDatabase() == null)) { + OOError.noDataBaseIsOpenForCiting() + .setTitle(errorDialogTitle) + .showErrorDialog(dialogService); + return; + } + + final BibDatabase database = stateManager.getActiveDatabase().get().getDatabase(); + if (database == null) { + OOError.noDataBaseIsOpenForCiting() + .setTitle(errorDialogTitle) + .showErrorDialog(dialogService); + return; + } + + List entries = stateManager.getSelectedEntries(); + if (entries.isEmpty()) { + OOError.noEntriesSelectedForCitation() + .setTitle(errorDialogTitle) + .showErrorDialog(dialogService); + return; + } + + if (getOrUpdateTheStyle(errorDialogTitle)) { return; } - Boolean inParenthesis = inParenthesisIn; String pageInfo = null; if (addPageInfo) { + boolean withText = citationType.withText(); Optional citeDialogViewModel = dialogService.showCustomDialogAndWait(new AdvancedCiteDialogView()); if (citeDialogViewModel.isPresent()) { @@ -496,43 +470,29 @@ private void pushEntries(boolean inParenthesisIn, boolean withText, boolean addP if (!model.pageInfoProperty().getValue().isEmpty()) { pageInfo = model.pageInfoProperty().getValue(); } - inParenthesis = model.citeInParProperty().getValue(); + citationType = citationTypeFromOptions(withText, model.citeInParProperty().getValue()); + } else { + // user canceled + return; } } - Optional databaseContext = stateManager.getActiveDatabase(); - if (databaseContext.isPresent()) { - final BibDatabase database = databaseContext.get().getDatabase(); - final List entries = stateManager.getSelectedEntries(); - if (!entries.isEmpty() && checkThatEntriesHaveKeys(entries)) { - - try { - if (style == null) { - style = loader.getUsedStyle(); - } - ooBase.insertEntry(entries, database, getBaseList(), style, inParenthesis, withText, pageInfo, - ooPrefs.getSyncWhenCiting()); - } catch (FileNotFoundException ex) { - - dialogService.showErrorDialogAndWait( - Localization.lang("No valid style file defined"), - Localization.lang("You must select either a valid style file, or use one of the default styles.")); - - LOGGER.warn("Problem with style file", ex); - } catch (ConnectionLostException ex) { - showConnectionLostErrorMessage(); - } catch (UndefinedCharacterFormatException ex) { - reportUndefinedCharacterFormat(ex); - } catch (UndefinedParagraphFormatException ex) { - reportUndefinedParagraphFormat(ex); - } catch (com.sun.star.lang.IllegalArgumentException | UnknownPropertyException | PropertyVetoException | - CreationException | NoSuchElementException | WrappedTargetException | IOException | - BibEntryNotFoundException | IllegalTypeException | PropertyExistException | - NotRemoveableException ex) { - LOGGER.warn("Could not insert entry", ex); - } - } + if (!checkThatEntriesHaveKeys(entries)) { + // Not all entries have keys and key generation was declined. + return; } + + Optional syncOptions = + (openOfficePreferences.getSyncWhenCiting() + ? Optional.of(new Update.SyncOptions(getBaseList())) + : Optional.empty()); + + ooBase.guiActionInsertEntry(entries, + database, + style, + citationType, + pageInfo, + syncOptions); } /** @@ -587,33 +547,12 @@ private boolean checkThatEntriesHaveKeys(List entries) { } } - private void showConnectionLostErrorMessage() { - dialogService.showErrorDialogAndWait(Localization.lang("Connection lost"), - Localization.lang("Connection to OpenOffice/LibreOffice has been lost. " + "Please make sure OpenOffice/LibreOffice is running, and try to reconnect.")); - } - - private void reportUndefinedParagraphFormat(UndefinedParagraphFormatException ex) { - dialogService.showErrorDialogAndWait(Localization.lang("Undefined paragraph format"), - Localization.lang("Your style file specifies the paragraph format '%0', " - + "which is undefined in your current OpenOffice/LibreOffice document.", - ex.getFormatName()) + "\n" + Localization.lang("The paragraph format is controlled by the property 'ReferenceParagraphFormat' or 'ReferenceHeaderParagraphFormat' in the style file.")); - } - - private void reportUndefinedCharacterFormat(UndefinedCharacterFormatException ex) { - dialogService.showErrorDialogAndWait(Localization.lang("Undefined character format"), - Localization.lang("Your style file specifies the character format '%0', " - + "which is undefined in your current OpenOffice/LibreOffice document.", - ex.getFormatName()) + "\n" + Localization.lang("The character format is controlled by the citation property 'CitationCharacterFormat' in the style file.") - - ); - } - private ContextMenu createSettingsPopup() { ContextMenu contextMenu = new ContextMenu(); CheckMenuItem autoSync = new CheckMenuItem(Localization.lang("Automatically sync bibliography when inserting citations")); - autoSync.selectedProperty().set(ooPrefs.getSyncWhenCiting()); + autoSync.selectedProperty().set(openOfficePreferences.getSyncWhenCiting()); ToggleGroup toggleGroup = new ToggleGroup(); RadioMenuItem useActiveBase = new RadioMenuItem(Localization.lang("Look up BibTeX entries in the active tab only")); @@ -623,31 +562,37 @@ private ContextMenu createSettingsPopup() { MenuItem clearConnectionSettings = new MenuItem(Localization.lang("Clear connection settings")); - if (ooPrefs.getUseAllDatabases()) { + if (openOfficePreferences.getUseAllDatabases()) { useAllBases.setSelected(true); } else { useActiveBase.setSelected(true); } autoSync.setOnAction(e -> { - ooPrefs.setSyncWhenCiting(autoSync.isSelected()); - preferencesService.setOpenOfficePreferences(ooPrefs); + openOfficePreferences.setSyncWhenCiting(autoSync.isSelected()); + preferencesService.setOpenOfficePreferences(openOfficePreferences); }); useAllBases.setOnAction(e -> { - ooPrefs.setUseAllDatabases(useAllBases.isSelected()); - preferencesService.setOpenOfficePreferences(ooPrefs); + openOfficePreferences.setUseAllDatabases(useAllBases.isSelected()); + preferencesService.setOpenOfficePreferences(openOfficePreferences); }); useActiveBase.setOnAction(e -> { - ooPrefs.setUseAllDatabases(!useActiveBase.isSelected()); - preferencesService.setOpenOfficePreferences(ooPrefs); + openOfficePreferences.setUseAllDatabases(!useActiveBase.isSelected()); + preferencesService.setOpenOfficePreferences(openOfficePreferences); }); clearConnectionSettings.setOnAction(e -> { - ooPrefs.clearConnectionSettings(); + openOfficePreferences.clearConnectionSettings(); dialogService.notify(Localization.lang("Cleared connection settings")); - preferencesService.setOpenOfficePreferences(ooPrefs); + preferencesService.setOpenOfficePreferences(openOfficePreferences); }); - contextMenu.getItems().addAll(autoSync, new SeparatorMenuItem(), useActiveBase, useAllBases, new SeparatorMenuItem(), clearConnectionSettings); + contextMenu.getItems().addAll( + autoSync, + new SeparatorMenuItem(), + useActiveBase, + useAllBases, + new SeparatorMenuItem(), + clearConnectionSettings); return contextMenu; } diff --git a/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogView.java b/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogView.java index 6792d2fbb8e..5435dda2abd 100644 --- a/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogView.java +++ b/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogView.java @@ -36,6 +36,7 @@ public class StyleSelectDialogView extends BaseDialog { private final MenuItem edit = new MenuItem(Localization.lang("Edit")); private final MenuItem reload = new MenuItem(Localization.lang("Reload")); private final StyleLoader loader; + @FXML private TableColumn colName; @FXML private TableView tvStyles; @FXML private TableColumn colJournals; diff --git a/src/main/java/org/jabref/gui/openoffice/UndefinedCharacterFormatException.java b/src/main/java/org/jabref/gui/openoffice/UndefinedCharacterFormatException.java deleted file mode 100644 index 4ab29ca2fa5..00000000000 --- a/src/main/java/org/jabref/gui/openoffice/UndefinedCharacterFormatException.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.jabref.gui.openoffice; - -/** - * Exception used to indicate that the plugin attempted to set a character format that is - * not defined in the current OpenOffice document. - */ -class UndefinedCharacterFormatException extends Exception { - - private final String formatName; - - public UndefinedCharacterFormatException(String formatName) { - super(); - this.formatName = formatName; - } - - public String getFormatName() { - return formatName; - } -} diff --git a/src/main/java/org/jabref/logic/openoffice/NoDocumentFoundException.java b/src/main/java/org/jabref/logic/openoffice/NoDocumentFoundException.java new file mode 100644 index 00000000000..2759a9931aa --- /dev/null +++ b/src/main/java/org/jabref/logic/openoffice/NoDocumentFoundException.java @@ -0,0 +1,12 @@ +package org.jabref.logic.openoffice; + +public class NoDocumentFoundException extends Exception { + + public NoDocumentFoundException(String message) { + super(message); + } + + public NoDocumentFoundException() { + super("No Writer documents found"); + } +} diff --git a/src/main/java/org/jabref/logic/openoffice/OOUtil.java b/src/main/java/org/jabref/logic/openoffice/OOUtil.java deleted file mode 100644 index a5976cb3342..00000000000 --- a/src/main/java/org/jabref/logic/openoffice/OOUtil.java +++ /dev/null @@ -1,284 +0,0 @@ -package org.jabref.logic.openoffice; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.jabref.architecture.AllowedToUseAwt; -import org.jabref.logic.layout.Layout; -import org.jabref.model.database.BibDatabase; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.field.Field; -import org.jabref.model.entry.field.UnknownField; - -import com.sun.star.beans.PropertyVetoException; -import com.sun.star.beans.UnknownPropertyException; -import com.sun.star.beans.XPropertySet; -import com.sun.star.lang.WrappedTargetException; -import com.sun.star.text.ControlCharacter; -import com.sun.star.text.XParagraphCursor; -import com.sun.star.text.XText; -import com.sun.star.text.XTextCursor; -import com.sun.star.uno.UnoRuntime; - -/** - * Utility methods for processing OO Writer documents. - */ -@AllowedToUseAwt("Requires AWT for changing document properties") -public class OOUtil { - - private static final String CHAR_STRIKEOUT = "CharStrikeout"; - private static final String CHAR_UNDERLINE = "CharUnderline"; - private static final String PARA_STYLE_NAME = "ParaStyleName"; - private static final String CHAR_CASE_MAP = "CharCaseMap"; - private static final String CHAR_POSTURE = "CharPosture"; - private static final String CHAR_WEIGHT = "CharWeight"; - private static final String CHAR_ESCAPEMENT_HEIGHT = "CharEscapementHeight"; - private static final String CHAR_ESCAPEMENT = "CharEscapement"; - - public enum Formatting { - BOLD, - ITALIC, - SMALLCAPS, - SUPERSCRIPT, - SUBSCRIPT, - UNDERLINE, - STRIKEOUT, - MONOSPACE - } - - private static final Pattern HTML_TAG = Pattern.compile(""); - - private static final Field UNIQUEFIER_FIELD = new UnknownField("uniq"); - - private OOUtil() { - // Just to hide the public constructor - } - - /** - * Insert a reference, formatted using a Layout, at the position of a given cursor. - * - * @param text The text to insert in. - * @param cursor The cursor giving the insert location. - * @param layout The Layout to format the reference with. - * @param parStyle The name of the paragraph style to use. - * @param entry The entry to insert. - * @param database The database the entry belongs to. - * @param uniquefier Uniqiefier letter, if any, to append to the entry's year. - */ - public static void insertFullReferenceAtCurrentLocation(XText text, XTextCursor cursor, - Layout layout, String parStyle, BibEntry entry, BibDatabase database, String uniquefier) - throws UndefinedParagraphFormatException, UnknownPropertyException, PropertyVetoException, - WrappedTargetException, IllegalArgumentException { - - // Backup the value of the uniq field, just in case the entry already has it: - Optional oldUniqVal = entry.getField(UNIQUEFIER_FIELD); - - // Set the uniq field with the supplied uniquefier: - if (uniquefier == null) { - entry.clearField(UNIQUEFIER_FIELD); - } else { - entry.setField(UNIQUEFIER_FIELD, uniquefier); - } - - // Do the layout for this entry: - String formattedText = layout.doLayout(entry, database); - - // Afterwards, reset the old value: - if (oldUniqVal.isPresent()) { - entry.setField(UNIQUEFIER_FIELD, oldUniqVal.get()); - } else { - entry.clearField(UNIQUEFIER_FIELD); - } - - // Insert the formatted text: - OOUtil.insertOOFormattedTextAtCurrentLocation(text, cursor, formattedText, parStyle); - } - - /** - * Insert a text with formatting indicated by HTML-like tags, into a text at the position given by a cursor. - * - * @param text The text to insert in. - * @param cursor The cursor giving the insert location. - * @param lText The marked-up text to insert. - * @param parStyle The name of the paragraph style to use. - * @throws WrappedTargetException - * @throws PropertyVetoException - * @throws UnknownPropertyException - * @throws IllegalArgumentException - */ - public static void insertOOFormattedTextAtCurrentLocation(XText text, XTextCursor cursor, String lText, - String parStyle) throws UndefinedParagraphFormatException, UnknownPropertyException, PropertyVetoException, - WrappedTargetException, IllegalArgumentException { - - XParagraphCursor parCursor = UnoRuntime.queryInterface( - XParagraphCursor.class, cursor); - XPropertySet props = UnoRuntime.queryInterface( - XPropertySet.class, parCursor); - - try { - props.setPropertyValue(PARA_STYLE_NAME, parStyle); - } catch (com.sun.star.lang.IllegalArgumentException ex) { - throw new UndefinedParagraphFormatException(parStyle); - } - - List formatting = new ArrayList<>(); - // We need to extract formatting. Use a simple regexp search iteration: - int piv = 0; - Matcher matcher = OOUtil.HTML_TAG.matcher(lText); - while (matcher.find()) { - String currentSubstring = lText.substring(piv, matcher.start()); - if (!currentSubstring.isEmpty()) { - OOUtil.insertTextAtCurrentLocation(text, cursor, currentSubstring, formatting); - } - String tag = matcher.group(); - // Handle tags: - if ("".equals(tag)) { - formatting.add(Formatting.BOLD); - } else if ("".equals(tag)) { - formatting.remove(Formatting.BOLD); - } else if ("".equals(tag) || "".equals(tag)) { - formatting.add(Formatting.ITALIC); - } else if ("".equals(tag) || "".equals(tag)) { - formatting.remove(Formatting.ITALIC); - } else if ("".equals(tag)) { - formatting.add(Formatting.MONOSPACE); - } else if ("".equals(tag)) { - formatting.remove(Formatting.MONOSPACE); - } else if ("".equals(tag)) { - formatting.add(Formatting.SMALLCAPS); - } else if ("".equals(tag)) { - formatting.remove(Formatting.SMALLCAPS); - } else if ("".equals(tag)) { - formatting.add(Formatting.SUPERSCRIPT); - } else if ("".equals(tag)) { - formatting.remove(Formatting.SUPERSCRIPT); - } else if ("".equals(tag)) { - formatting.add(Formatting.SUBSCRIPT); - } else if ("".equals(tag)) { - formatting.remove(Formatting.SUBSCRIPT); - } else if ("".equals(tag)) { - formatting.add(Formatting.UNDERLINE); - } else if ("".equals(tag)) { - formatting.remove(Formatting.UNDERLINE); - } else if ("".equals(tag)) { - formatting.add(Formatting.STRIKEOUT); - } else if ("".equals(tag)) { - formatting.remove(Formatting.STRIKEOUT); - } - - piv = matcher.end(); - } - - if (piv < lText.length()) { - OOUtil.insertTextAtCurrentLocation(text, cursor, lText.substring(piv), formatting); - } - - cursor.collapseToEnd(); - } - - public static void insertParagraphBreak(XText text, XTextCursor cursor) throws IllegalArgumentException { - text.insertControlCharacter(cursor, ControlCharacter.PARAGRAPH_BREAK, true); - cursor.collapseToEnd(); - } - - public static void insertTextAtCurrentLocation(XText text, XTextCursor cursor, String string, - List formatting) - throws UnknownPropertyException, PropertyVetoException, WrappedTargetException, - IllegalArgumentException { - text.insertString(cursor, string, true); - // Access the property set of the cursor, and set the currently selected text - // (which is the string we just inserted) to be bold - XPropertySet xCursorProps = UnoRuntime.queryInterface( - XPropertySet.class, cursor); - if (formatting.contains(Formatting.BOLD)) { - xCursorProps.setPropertyValue(CHAR_WEIGHT, - com.sun.star.awt.FontWeight.BOLD); - } else { - xCursorProps.setPropertyValue(CHAR_WEIGHT, - com.sun.star.awt.FontWeight.NORMAL); - } - - if (formatting.contains(Formatting.ITALIC)) { - xCursorProps.setPropertyValue(CHAR_POSTURE, - com.sun.star.awt.FontSlant.ITALIC); - } else { - xCursorProps.setPropertyValue(CHAR_POSTURE, - com.sun.star.awt.FontSlant.NONE); - } - - if (formatting.contains(Formatting.SMALLCAPS)) { - xCursorProps.setPropertyValue(CHAR_CASE_MAP, - com.sun.star.style.CaseMap.SMALLCAPS); - } else { - xCursorProps.setPropertyValue(CHAR_CASE_MAP, - com.sun.star.style.CaseMap.NONE); - } - - // TODO: the tag doesn't work - /* - if (formatting.contains(Formatting.MONOSPACE)) { - xCursorProps.setPropertyValue("CharFontPitch", - com.sun.star.awt.FontPitch.FIXED); - } - else { - xCursorProps.setPropertyValue("CharFontPitch", - com.sun.star.awt.FontPitch.VARIABLE); - } */ - if (formatting.contains(Formatting.SUBSCRIPT)) { - xCursorProps.setPropertyValue(CHAR_ESCAPEMENT, - (byte) -101); - xCursorProps.setPropertyValue(CHAR_ESCAPEMENT_HEIGHT, - (byte) 58); - } else if (formatting.contains(Formatting.SUPERSCRIPT)) { - xCursorProps.setPropertyValue(CHAR_ESCAPEMENT, - (byte) 101); - xCursorProps.setPropertyValue(CHAR_ESCAPEMENT_HEIGHT, - (byte) 58); - } else { - xCursorProps.setPropertyValue(CHAR_ESCAPEMENT, - (byte) 0); - xCursorProps.setPropertyValue(CHAR_ESCAPEMENT_HEIGHT, - (byte) 100); - } - - if (formatting.contains(Formatting.UNDERLINE)) { - xCursorProps.setPropertyValue(CHAR_UNDERLINE, com.sun.star.awt.FontUnderline.SINGLE); - } else { - xCursorProps.setPropertyValue(CHAR_UNDERLINE, com.sun.star.awt.FontUnderline.NONE); - } - - if (formatting.contains(Formatting.STRIKEOUT)) { - xCursorProps.setPropertyValue(CHAR_STRIKEOUT, com.sun.star.awt.FontStrikeout.SINGLE); - } else { - xCursorProps.setPropertyValue(CHAR_STRIKEOUT, com.sun.star.awt.FontStrikeout.NONE); - } - cursor.collapseToEnd(); - } - - public static void insertTextAtCurrentLocation(XText text, XTextCursor cursor, String string, String parStyle) - throws WrappedTargetException, PropertyVetoException, UnknownPropertyException, - UndefinedParagraphFormatException { - text.insertString(cursor, string, true); - XParagraphCursor parCursor = UnoRuntime.queryInterface( - XParagraphCursor.class, cursor); - // Access the property set of the cursor, and set the currently selected text - // (which is the string we just inserted) to be bold - XPropertySet props = UnoRuntime.queryInterface( - XPropertySet.class, parCursor); - try { - props.setPropertyValue(PARA_STYLE_NAME, parStyle); - } catch (IllegalArgumentException ex) { - throw new UndefinedParagraphFormatException(parStyle); - } - cursor.collapseToEnd(); - } - - public static Object getProperty(Object object, String property) - throws UnknownPropertyException, WrappedTargetException { - XPropertySet props = UnoRuntime.queryInterface(XPropertySet.class, object); - return props.getPropertyValue(property); - } -} diff --git a/src/main/java/org/jabref/logic/openoffice/OpenOfficeFileSearch.java b/src/main/java/org/jabref/logic/openoffice/OpenOfficeFileSearch.java index 309096b08cf..dd59b8a09bf 100644 --- a/src/main/java/org/jabref/logic/openoffice/OpenOfficeFileSearch.java +++ b/src/main/java/org/jabref/logic/openoffice/OpenOfficeFileSearch.java @@ -43,8 +43,9 @@ public static List detectInstallations() { private static List findOpenOfficeDirectories(List programDirectories) { - BiPredicate filePredicate = (path, attr) -> attr.isDirectory() && (path.toString().toLowerCase(Locale.ROOT).contains("openoffice") - || path.toString().toLowerCase(Locale.ROOT).contains("libreoffice")); + BiPredicate filePredicate = (path, attr) -> + attr.isDirectory() && (path.toString().toLowerCase(Locale.ROOT).contains("openoffice") + || path.toString().toLowerCase(Locale.ROOT).contains("libreoffice")); return programDirectories.stream().flatMap(dirs -> { try { diff --git a/src/main/java/org/jabref/logic/openoffice/UndefinedBibtexEntry.java b/src/main/java/org/jabref/logic/openoffice/UndefinedBibtexEntry.java deleted file mode 100644 index 8a1a7cf5e8f..00000000000 --- a/src/main/java/org/jabref/logic/openoffice/UndefinedBibtexEntry.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.jabref.logic.openoffice; - -import org.jabref.logic.openoffice.style.OOBibStyle; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.field.StandardField; - -/** - * Subclass of BibEntry for representing entries referenced in a document that can't be found in JabRef's current database. - */ -public class UndefinedBibtexEntry extends BibEntry { - - private final String key; - - public UndefinedBibtexEntry(String key) { - this.key = key; - setField(StandardField.AUTHOR, OOBibStyle.UNDEFINED_CITATION_MARKER); - } - - public String getKey() { - return key; - } -} diff --git a/src/main/java/org/jabref/logic/openoffice/UndefinedParagraphFormatException.java b/src/main/java/org/jabref/logic/openoffice/UndefinedParagraphFormatException.java deleted file mode 100644 index 361f8d44f54..00000000000 --- a/src/main/java/org/jabref/logic/openoffice/UndefinedParagraphFormatException.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.jabref.logic.openoffice; - -/** - * Exception used to indicate that the plugin attempted to set a paragraph format that is not defined in the current OpenOffice document. - */ -public class UndefinedParagraphFormatException extends Exception { - - private final String formatName; - - public UndefinedParagraphFormatException(String formatName) { - super(); - this.formatName = formatName; - } - - public String getFormatName() { - return formatName; - } -} diff --git a/src/main/java/org/jabref/logic/openoffice/action/EditInsert.java b/src/main/java/org/jabref/logic/openoffice/action/EditInsert.java index 0004d3fe6db..9c171553a4b 100644 --- a/src/main/java/org/jabref/logic/openoffice/action/EditInsert.java +++ b/src/main/java/org/jabref/logic/openoffice/action/EditInsert.java @@ -32,17 +32,14 @@ public class EditInsert { private EditInsert() { - /**/ } /** * In insertEntry we receive BibEntry values from the GUI. - * + *

* In the document we store citations by their citation key. - * - * If the citation key is missing, the best we can do is to notify the user. Or the programmer, - * that we cannot accept such input. - * + *

+ * If the citation key is missing, the best we can do is to notify the user. Or the programmer, that we cannot accept such input. */ private static String insertEntryGetCitationKey(BibEntry entry) { Optional key = entry.getCitationKey(); @@ -53,7 +50,7 @@ private static String insertEntryGetCitationKey(BibEntry entry) { } /** - * @param cursor Where to insert. + * @param cursor Where to insert. * @param pageInfo A single pageInfo for a list of entries. This is what we get from the GUI. */ public static void insertCitationGroup(XTextDocument doc, @@ -64,13 +61,13 @@ public static void insertCitationGroup(XTextDocument doc, OOBibStyle style, CitationType citationType, String pageInfo) - throws - NoDocumentException, - NotRemoveableException, - WrappedTargetException, - PropertyVetoException, - CreationException, - IllegalTypeException { + throws + NoDocumentException, + NotRemoveableException, + WrappedTargetException, + PropertyVetoException, + CreationException, + IllegalTypeException { List citationKeys = OOListUtil.map(entries, EditInsert::insertEntryGetCitationKey); @@ -86,13 +83,13 @@ public static void insertCitationGroup(XTextDocument doc, } // The text we insert - OOText citeText = null; + OOText citeText; if (style.isNumberEntries()) { citeText = OOText.fromString("[-]"); // A dash only. Only refresh later. } else { citeText = style.createCitationMarker(citations, - citationType.inParenthesis(), - NonUniqueCitationMarker.FORGIVEN); + citationType.inParenthesis(), + NonUniqueCitationMarker.FORGIVEN); } if (StringUtil.isBlank(OOText.toString(citeText))) { @@ -102,17 +99,16 @@ public static void insertCitationGroup(XTextDocument doc, try { UnoScreenRefresh.lockControllers(doc); UpdateCitationMarkers.createAndFillCitationGroup(frontend, - doc, - citationKeys, - pageInfos, - citationType, - citeText, - cursor, - style, - true /* insertSpaceAfter */); + doc, + citationKeys, + pageInfos, + citationType, + citeText, + cursor, + style, + true /* insertSpaceAfter */); } finally { UnoScreenRefresh.unlockControllers(doc); } - } } diff --git a/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java b/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java index f91e8aec93d..dc2058a9107 100644 --- a/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java +++ b/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java @@ -34,21 +34,20 @@ public class EditMerge { private static final Logger LOGGER = LoggerFactory.getLogger(EditMerge.class); private EditMerge() { - // hide constructor } - /* + /** * @return true if modified document */ public static boolean mergeCitationGroups(XTextDocument doc, OOFrontend frontend, OOBibStyle style) - throws - CreationException, - IllegalArgumentException, - IllegalTypeException, - NoDocumentException, - NotRemoveableException, - PropertyVetoException, - WrappedTargetException { + throws + CreationException, + IllegalArgumentException, + IllegalTypeException, + NoDocumentException, + NotRemoveableException, + PropertyVetoException, + WrappedTargetException { boolean madeModifications; @@ -62,8 +61,8 @@ public static boolean mergeCitationGroups(XTextDocument doc, OOFrontend frontend List groups = joinableGroupData.group; List newCitations = (groups.stream() - .flatMap(group -> group.citationsInStorageOrder.stream()) - .collect(Collectors.toList())); + .flatMap(group -> group.citationsInStorageOrder.stream()) + .collect(Collectors.toList())); CitationType citationType = groups.get(0).citationType; List> pageInfos = frontend.backend.combinePageInfos(groups); @@ -77,18 +76,17 @@ public static boolean mergeCitationGroups(XTextDocument doc, OOFrontend frontend /* insertSpaceAfter: no, it is already there (or could be) */ boolean insertSpaceAfter = false; UpdateCitationMarkers.createAndFillCitationGroup(frontend, - doc, - citationKeys, - pageInfos, - citationType, - OOText.fromString("tmp"), - textCursor, - style, - insertSpaceAfter); + doc, + citationKeys, + pageInfos, + citationType, + OOText.fromString("tmp"), + textCursor, + style, + insertSpaceAfter); } madeModifications = !joinableGroups.isEmpty(); - } finally { UnoScreenRefresh.unlockControllers(doc); } @@ -97,10 +95,14 @@ public static boolean mergeCitationGroups(XTextDocument doc, OOFrontend frontend } private static class JoinableGroupData { - /** A list of consecutive citation groups only separated by spaces. */ + /** + * A list of consecutive citation groups only separated by spaces. + */ List group; - /** A cursor covering the XTextRange of each entry in group (and the spaces between them) */ + /** + * A cursor covering the XTextRange of each entry in group (and the spaces between them) + */ XTextCursor groupCursor; JoinableGroupData(List group, XTextCursor groupCursor) { @@ -119,8 +121,8 @@ private static class ScanState { // null if currentGroup.isEmpty() XTextCursor currentGroupCursor; - // A cursor starting at the end of the last CitationGroup in - // currentGroup. null if currentGroup.isEmpty() + // A cursor starting at the end of the last CitationGroup in currentGroup. + // null if currentGroup.isEmpty() XTextCursor cursorBetween; // The last element of currentGroup. @@ -147,11 +149,9 @@ void reset() { /** * Decide if group could be added to state.currentGroup * - * @param group The CitationGroup to test + * @param group The CitationGroup to test * @param currentRange The XTextRange corresponding to group. - * - * @return false if cannot add, true if can. If returned true, then state.cursorBetween and - * state.currentGroupCursor are expanded to end at the start of currentRange. + * @return false if cannot add, true if can. If returned true, then state.cursorBetween and state.currentGroupCursor are expanded to end at the start of currentRange. */ private static boolean checkAddToGroup(ScanState state, CitationGroup group, XTextRange currentRange) { @@ -184,14 +184,14 @@ private static boolean checkAddToGroup(ScanState state, CitationGroup group, XTe int textOrder = UnoTextRange.compareStarts(state.prevRange, currentRange); if (textOrder != (-1)) { String msg = - String.format("MergeCitationGroups:" - + " \"%s\" supposed to be followed by \"%s\"," - + " but %s", - state.prevRange.getString(), - currentRange.getString(), - ((textOrder == 0) - ? "they start at the same position" - : "the start of the latter precedes the start of the first")); + String.format("MergeCitationGroups:" + + " \"%s\" supposed to be followed by \"%s\"," + + " but %s", + state.prevRange.getString(), + currentRange.getString(), + ((textOrder == 0) + ? "they start at the same position" + : "the start of the latter precedes the start of the first")); LOGGER.warn(msg); return false; } @@ -217,7 +217,7 @@ private static boolean checkAddToGroup(ScanState state, CitationGroup group, XTe XTextRange rangeStart = currentRange.getStart(); boolean couldExpand = true; XTextCursor thisCharCursor = - (currentRange.getText().createTextCursorByRange(state.cursorBetween.getEnd())); + (currentRange.getText().createTextCursorByRange(state.cursorBetween.getEnd())); while (couldExpand && (UnoTextRange.compareEnds(state.cursorBetween, rangeStart) < 0)) { // @@ -247,10 +247,7 @@ private static boolean checkAddToGroup(ScanState state, CitationGroup group, XTe } /** - * Add group to state.currentGroup - * Set state.cursorBetween to start at currentRange.getEnd() - * Expand state.currentGroupCursor to also cover currentRange - * Set state.prev to group, state.prevRange to currentRange + * Add group to state.currentGroup Set state.cursorBetween to start at currentRange.getEnd() Expand state.currentGroupCursor to also cover currentRange Set state.prev to group, state.prevRange to currentRange */ private static void addToCurrentGroup(ScanState state, CitationGroup group, XTextRange currentRange) { final boolean isNewGroup = state.currentGroup.isEmpty(); @@ -271,7 +268,7 @@ private static void addToCurrentGroup(ScanState state, CitationGroup group, XTex // If new group, create currentGroupCursor if (isNewGroup) { state.currentGroupCursor = (currentRange.getText() - .createTextCursorByRange(currentRange.getStart())); + .createTextCursorByRange(currentRange.getStart())); } // include currentRange in currentGroupCursor @@ -288,12 +285,12 @@ private static void addToCurrentGroup(ScanState state, CitationGroup group, XTex } /** - * Scan the document for joinable groups. Return those found. + * Scan the document for joinable groups. Return those found. */ private static List scan(XTextDocument doc, OOFrontend frontend) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { List result = new ArrayList<>(); List groups = frontend.getCitationGroupsSortedWithinPartitions(doc, false /* mapFootnotesToFootnoteMarks */); @@ -306,7 +303,7 @@ private static List scan(XTextDocument doc, OOFrontend fronte for (CitationGroup group : groups) { XTextRange currentRange = (frontend.getMarkRange(doc, group) - .orElseThrow(IllegalStateException::new)); + .orElseThrow(IllegalStateException::new)); /* * Decide if we add group to the group. False when the group is empty. @@ -340,5 +337,4 @@ private static List scan(XTextDocument doc, OOFrontend fronte } return result; } - } diff --git a/src/main/java/org/jabref/logic/openoffice/action/EditSeparate.java b/src/main/java/org/jabref/logic/openoffice/action/EditSeparate.java index 66630a6f8d0..101dffb813a 100644 --- a/src/main/java/org/jabref/logic/openoffice/action/EditSeparate.java +++ b/src/main/java/org/jabref/logic/openoffice/action/EditSeparate.java @@ -25,21 +25,20 @@ public class EditSeparate { private EditSeparate() { - /**/ } public static boolean separateCitations(XTextDocument doc, OOFrontend frontend, List databases, OOBibStyle style) - throws - CreationException, - IllegalTypeException, - NoDocumentException, - NotRemoveableException, - PropertyVetoException, - WrappedTargetException, - com.sun.star.lang.IllegalArgumentException { + throws + CreationException, + IllegalTypeException, + NoDocumentException, + NotRemoveableException, + PropertyVetoException, + WrappedTargetException, + com.sun.star.lang.IllegalArgumentException { boolean madeModifications = false; @@ -58,8 +57,8 @@ public static boolean separateCitations(XTextDocument doc, for (CitationGroup group : groups) { XTextRange range1 = (frontend - .getMarkRange(doc, group) - .orElseThrow(IllegalStateException::new)); + .getMarkRange(doc, group) + .orElseThrow(IllegalStateException::new)); XTextCursor textCursor = range1.getText().createTextCursorByRange(range1); List citations = group.citationsInStorageOrder; @@ -77,14 +76,14 @@ public static boolean separateCitations(XTextDocument doc, Citation citation = citations.get(i); UpdateCitationMarkers.createAndFillCitationGroup(frontend, - doc, - List.of(citation.citationKey), - List.of(citation.getPageInfo()), - group.citationType, - OOText.fromString(citation.citationKey), - textCursor, - style, - insertSpaceAfter); + doc, + List.of(citation.citationKey), + List.of(citation.getPageInfo()), + group.citationType, + OOText.fromString(citation.citationKey), + textCursor, + style, + insertSpaceAfter); textCursor.collapseToEnd(); } diff --git a/src/main/java/org/jabref/logic/openoffice/action/ExportCited.java b/src/main/java/org/jabref/logic/openoffice/action/ExportCited.java index c38cd4fd4cc..4bdc11a7f71 100644 --- a/src/main/java/org/jabref/logic/openoffice/action/ExportCited.java +++ b/src/main/java/org/jabref/logic/openoffice/action/ExportCited.java @@ -19,7 +19,6 @@ public class ExportCited { private ExportCited() { - /**/ } public static class GenerateDatabaseResult { @@ -36,20 +35,16 @@ public static class GenerateDatabaseResult { } /** - * * @param databases The databases to look up the citation keys in the document from. * @return A new database, with cloned entries. - * * If a key is not found, it is added to result.unresolvedKeys - * - * Cross references (in StandardField.CROSSREF) are followed (not recursively): - * If the referenced entry is found, it is included in the result. - * If it is not found, it is silently ignored. + *

+ * Cross references (in StandardField.CROSSREF) are followed (not recursively): If the referenced entry is found, it is included in the result. If it is not found, it is silently ignored. */ public static GenerateDatabaseResult generateDatabase(XTextDocument doc, List databases) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { OOFrontend frontend = new OOFrontend(doc); CitedKeys citationKeys = frontend.citationGroups.getCitedKeysUnordered(); @@ -64,7 +59,6 @@ public static GenerateDatabaseResult generateDatabase(XTextDocument doc, List { + .getField(StandardField.CROSSREF) + .ifPresent(crossReference -> { boolean isNew = !seen.contains(crossReference); if (isNew) { // Add it if it is in the current library loopDatabase - .getEntryByCitationKey(crossReference) - .ifPresent(entriesToInsert::add); + .getEntryByCitationKey(crossReference) + .ifPresent(entriesToInsert::add); seen.add(crossReference); } }); @@ -94,5 +88,4 @@ public static GenerateDatabaseResult generateDatabase(XTextDocument doc, List getCitationEntries(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { OOFrontend frontend = new OOFrontend(doc); return frontend.getCitationEntries(doc); } public static void applyCitationEntries(XTextDocument doc, List citationEntries) - throws - NoDocumentException, - PropertyVetoException, - IllegalTypeException, - WrappedTargetException, - IllegalArgumentException { + throws + NoDocumentException, + PropertyVetoException, + IllegalTypeException, + WrappedTargetException, + IllegalArgumentException { OOFrontend frontend = new OOFrontend(doc); frontend.applyCitationEntries(doc, citationEntries); } diff --git a/src/main/java/org/jabref/logic/openoffice/action/Update.java b/src/main/java/org/jabref/logic/openoffice/action/Update.java index 80263833aec..f5222c47ec0 100644 --- a/src/main/java/org/jabref/logic/openoffice/action/Update.java +++ b/src/main/java/org/jabref/logic/openoffice/action/Update.java @@ -21,10 +21,7 @@ */ public class Update { - static final boolean USE_LOCK_CONTROLLERS = true; - private Update() { - /**/ } /** @@ -37,17 +34,19 @@ private static List updateDocument(XTextDocument doc, FunctionalTextViewCursor fcursor, boolean doUpdateBibliography, boolean alwaysAddCitedOnPages) - throws - CreationException, - NoDocumentException, - WrappedTargetException, - com.sun.star.lang.IllegalArgumentException { + throws + CreationException, + NoDocumentException, + WrappedTargetException, + com.sun.star.lang.IllegalArgumentException { + + final boolean useLockControllers = true; frontend.imposeGlobalOrder(doc, fcursor); OOProcess.produceCitationMarkers(frontend.citationGroups, databases, style); try { - if (USE_LOCK_CONTROLLERS) { + if (useLockControllers) { UnoScreenRefresh.lockControllers(doc); } @@ -55,15 +54,15 @@ private static List updateDocument(XTextDocument doc, if (doUpdateBibliography) { UpdateBibliography.rebuildBibTextSection(doc, - frontend, - frontend.citationGroups.getBibliography().get(), - style, - alwaysAddCitedOnPages); + frontend, + frontend.citationGroups.getBibliography().get(), + style, + alwaysAddCitedOnPages); } return frontend.citationGroups.getUnresolvedKeys(); } finally { - if (USE_LOCK_CONTROLLERS && UnoScreenRefresh.hasControllersLocked(doc)) { + if (useLockControllers && UnoScreenRefresh.hasControllersLocked(doc)) { UnoScreenRefresh.unlockControllers(doc); } } @@ -97,37 +96,36 @@ public static List synchronizeDocument(XTextDocument doc, OOBibStyle style, FunctionalTextViewCursor fcursor, SyncOptions syncOptions) - throws - CreationException, - NoDocumentException, - WrappedTargetException, - com.sun.star.lang.IllegalArgumentException { + throws + CreationException, + NoDocumentException, + WrappedTargetException, + com.sun.star.lang.IllegalArgumentException { return Update.updateDocument(doc, - frontend, - syncOptions.databases, - style, - fcursor, - syncOptions.updateBibliography, - syncOptions.alwaysAddCitedOnPages); + frontend, + syncOptions.databases, + style, + fcursor, + syncOptions.updateBibliography, + syncOptions.alwaysAddCitedOnPages); } - /* + /** * Reread document before sync */ public static List resyncDocument(XTextDocument doc, OOBibStyle style, FunctionalTextViewCursor fcursor, SyncOptions syncOptions) - throws - CreationException, - NoDocumentException, - WrappedTargetException, - com.sun.star.lang.IllegalArgumentException { + throws + CreationException, + NoDocumentException, + WrappedTargetException, + com.sun.star.lang.IllegalArgumentException { OOFrontend frontend = new OOFrontend(doc); return Update.synchronizeDocument(doc, frontend, style, fcursor, syncOptions); } - } diff --git a/src/main/java/org/jabref/logic/openoffice/backend/Backend52.java b/src/main/java/org/jabref/logic/openoffice/backend/Backend52.java index 4a0d8d05e0e..e433d18e72f 100644 --- a/src/main/java/org/jabref/logic/openoffice/backend/Backend52.java +++ b/src/main/java/org/jabref/logic/openoffice/backend/Backend52.java @@ -37,9 +37,7 @@ import org.slf4j.LoggerFactory; /** - * Backend52, Codec52 and OODataModel.JabRef52 refer to the mode of storage, encoding and - * what-is-stored in the document under JabRef version 5.2. These basically did not change up to - * JabRef 5.4. + * Backend52, Codec52 and OODataModel.JabRef52 refer to the mode of storage, encoding and what-is-stored in the document under JabRef version 5.2. These basically did not change up to JabRef 5.4. */ public class Backend52 { private static final Logger LOGGER = LoggerFactory.getLogger(Backend52.class); @@ -55,25 +53,21 @@ public Backend52() { } /** - * Get reference mark names from the document matching the pattern - * used for JabRef reference mark names. - * + * Get reference mark names from the document matching the pattern used for JabRef reference mark names. + *

* Note: the names returned are in arbitrary order. - * */ public List getJabRefReferenceMarkNames(XTextDocument doc) - throws - NoDocumentException { + throws + NoDocumentException { List allNames = this.citationStorageManager.getUsedNames(doc); return Codec52.filterIsJabRefReferenceMarkName(allNames); } /** - * Names of custom properties belonging to us, but without a corresponding reference mark. - * These can be deleted. + * Names of custom properties belonging to us, but without a corresponding reference mark. These can be deleted. * * @param citationGroupNames These are the names that are used. - * */ private List findUnusedJabrefPropertyNames(XTextDocument doc, List citationGroupNames) { @@ -82,10 +76,10 @@ private List findUnusedJabrefPropertyNames(XTextDocument doc, List pageInfoThrash = new ArrayList<>(); List jabrefPropertyNames = - UnoUserDefinedProperty.getListOfNames(doc) - .stream() - .filter(Codec52::isJabRefReferenceMarkName) - .collect(Collectors.toList()); + UnoUserDefinedProperty.getListOfNames(doc) + .stream() + .filter(Codec52::isJabRefReferenceMarkName) + .toList(); for (String pn : jabrefPropertyNames) { if (!citationGroupNamesSet.contains(pn)) { pageInfoThrash.add(pn); @@ -95,21 +89,21 @@ private List findUnusedJabrefPropertyNames(XTextDocument doc, } /** - * @return Optional.empty if all is OK, message text otherwise. + * @return Optional.empty if all is OK, message text otherwise. */ public Optional healthReport(XTextDocument doc) - throws - NoDocumentException { + throws + NoDocumentException { List pageInfoThrash = - this.findUnusedJabrefPropertyNames(doc, this.getJabRefReferenceMarkNames(doc)); + this.findUnusedJabrefPropertyNames(doc, this.getJabRefReferenceMarkNames(doc)); if (pageInfoThrash.isEmpty()) { return Optional.empty(); } - StringBuilder msg = new StringBuilder("Backend52: found unused pageInfo data, with names listed below.\n"); - msg.append("In LibreOffice you may remove these in [File]/[Properties]/[Custom Properties]\n"); - msg.append(String.join("\n", pageInfoThrash)); - return Optional.of(msg.toString()); + String msg = "Backend52: found unused pageInfo data, with names listed below.\n" + + "In LibreOffice you may remove these in [File]/[Properties]/[Custom Properties]\n" + + String.join("\n", pageInfoThrash); + return Optional.of(msg); } private static void setPageInfoInDataInitial(List citations, Optional pageInfo) { @@ -131,51 +125,44 @@ private static Optional getPageInfoFromData(CitationGroup group) { * @param markName Reference mark name */ public CitationGroup readCitationGroupFromDocumentOrThrow(XTextDocument doc, String markName) - throws - WrappedTargetException, - NoDocumentException { + throws + WrappedTargetException, + NoDocumentException { Codec52.ParsedMarkName parsed = Codec52.parseMarkName(markName).orElseThrow(IllegalArgumentException::new); List citations = (parsed.citationKeys.stream() - .map(Citation::new) - .collect(Collectors.toList())); + .map(Citation::new) + .collect(Collectors.toList())); Optional pageInfo = (UnoUserDefinedProperty.getStringValue(doc, markName) - .map(OOText::fromString)); + .map(OOText::fromString)); pageInfo = PageInfo.normalizePageInfo(pageInfo); setPageInfoInDataInitial(citations, pageInfo); NamedRange namedRange = (citationStorageManager.getNamedRangeFromDocument(doc, markName) - .orElseThrow(IllegalArgumentException::new)); + .orElseThrow(IllegalArgumentException::new)); CitationGroupId groupId = new CitationGroupId(markName); CitationGroup group = new CitationGroup(OODataModel.JabRef52, - groupId, - parsed.citationType, - citations, - Optional.of(markName)); + groupId, + parsed.citationType, + citations, + Optional.of(markName)); this.cgidToNamedRange.put(groupId, namedRange); return group; } /** - * Create a reference mark at the end of {@code position} in the document. - * - * On return {@code position} is collapsed, and is after the inserted space, or at the end of - * the reference mark. + * Create a reference mark at the end of {@code position} in the document. + *

+ * On return {@code position} is collapsed, and is after the inserted space, or at the end of the reference mark. * - * @param citationKeys Keys to be cited. - * - * @param pageInfos An optional pageInfo for each citation key. - * Backend52 only uses and stores the last pageInfo, - * all others should be Optional.empty() - * - * @param position Collapsed to its end. - * - * @param insertSpaceAfter We insert a space after the mark, that carries on format of - * characters from the original position. + * @param citationKeys Keys to be cited. + * @param pageInfos An optional pageInfo for each citation key. Backend52 only uses and stores the last pageInfo, all others should be Optional.empty() + * @param position Collapsed to its end. + * @param insertSpaceAfter We insert a space after the mark, that carries on format of characters from the original position. */ public CitationGroup createCitationGroup(XTextDocument doc, List citationKeys, @@ -183,13 +170,13 @@ public CitationGroup createCitationGroup(XTextDocument doc, CitationType citationType, XTextCursor position, boolean insertSpaceAfter) - throws - CreationException, - NoDocumentException, - WrappedTargetException, - NotRemoveableException, - PropertyVetoException, - IllegalTypeException { + throws + CreationException, + NoDocumentException, + WrappedTargetException, + NotRemoveableException, + PropertyVetoException, + IllegalTypeException { Objects.requireNonNull(pageInfos); if (pageInfos.size() != citationKeys.size()) { @@ -213,7 +200,7 @@ public CitationGroup createCitationGroup(XTextDocument doc, } else { if (pageInfo.isPresent()) { LOGGER.warn("dataModel JabRef52" - + " only supports pageInfo for the last citation of a group"); + + " only supports pageInfo for the last citation of a group"); } } break; @@ -231,8 +218,8 @@ public CitationGroup createCitationGroup(XTextDocument doc, * has to be unique in the document. */ final String markName = Codec52.getUniqueMarkName(new HashSet<>(citationStorageManager.getUsedNames(doc)), - citationKeys, - citationType); + citationKeys, + citationType); final CitationGroupId groupId = new CitationGroupId(markName); @@ -241,10 +228,10 @@ public CitationGroup createCitationGroup(XTextDocument doc, */ boolean withoutBrackets = (citationType == CitationType.INVISIBLE_CIT); NamedRange namedRange = this.citationStorageManager.createNamedRange(doc, - markName, - position, - insertSpaceAfter, - withoutBrackets); + markName, + position, + insertSpaceAfter, + withoutBrackets); switch (dataModel) { case JabRef52: @@ -258,9 +245,9 @@ public CitationGroup createCitationGroup(XTextDocument doc, UnoUserDefinedProperty.removeIfExists(doc, markName); } CitationGroup group = new CitationGroup(OODataModel.JabRef52, - groupId, - citationType, citations, - Optional.of(markName)); + groupId, + citationType, citations, + Optional.of(markName)); this.cgidToNamedRange.put(groupId, namedRange); return group; case JabRef60: @@ -272,8 +259,7 @@ public CitationGroup createCitationGroup(XTextDocument doc, /** * @return A list with a nullable pageInfo entry for each citation in joinableGroups. - * - * TODO: JabRef52 combinePageInfos is not reversible. Should warn user to check the result. Or + * TODO: JabRef52 combinePageInfos is not reversible. Should warn user to check the result. Or * ask what to do. */ public static List> @@ -282,18 +268,18 @@ public CitationGroup createCitationGroup(XTextDocument doc, case JabRef52: // collect to pageInfos List> pageInfos = OOListUtil.map(joinableGroup, - Backend52::getPageInfoFromData); + Backend52::getPageInfoFromData); // Try to do something of the pageInfos. String singlePageInfo = (pageInfos.stream() - .filter(Optional::isPresent) - .map(pi -> OOText.toString(pi.get())) - .distinct() - .collect(Collectors.joining("; "))); + .filter(Optional::isPresent) + .map(pi -> OOText.toString(pi.get())) + .distinct() + .collect(Collectors.joining("; "))); int totalCitations = (joinableGroup.stream() - .map(CitationGroup::numberOfCitations) - .mapToInt(Integer::intValue).sum()); + .map(CitationGroup::numberOfCitations) + .mapToInt(Integer::intValue).sum()); if ("".equals(singlePageInfo)) { singlePageInfo = null; } @@ -301,17 +287,14 @@ public CitationGroup createCitationGroup(XTextDocument doc, case JabRef60: return (joinableGroup.stream() - .flatMap(group -> (group.citationsInStorageOrder.stream() - .map(Citation::getPageInfo))) - .collect(Collectors.toList())); + .flatMap(group -> (group.citationsInStorageOrder.stream() + .map(Citation::getPageInfo))) + .collect(Collectors.toList())); default: throw new IllegalArgumentException("unhandled dataModel here"); } } - /** - * - */ public List> combinePageInfos(List joinableGroup) { return combinePageInfosCommon(this.dataModel, joinableGroup); } @@ -325,10 +308,10 @@ private NamedRange getNamedRangeOrThrow(CitationGroup group) { } public void removeCitationGroup(CitationGroup group, XTextDocument doc) - throws - WrappedTargetException, - NoDocumentException, - NotRemoveableException { + throws + WrappedTargetException, + NoDocumentException, + NotRemoveableException { NamedRange namedRange = getNamedRangeOrThrow(group); String refMarkName = namedRange.getRangeName(); namedRange.removeFromDocument(doc); @@ -340,22 +323,21 @@ public void removeCitationGroup(CitationGroup group, XTextDocument doc) * @return Optional.empty if the reference mark is missing. */ public Optional getMarkRange(CitationGroup group, XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { NamedRange namedRange = getNamedRangeOrThrow(group); return namedRange.getMarkRange(doc); } /** - * Cursor for the reference marks as is: not prepared for filling, but does not need - * cleanFillCursorForCitationGroup either. + * Cursor for the reference marks as is: not prepared for filling, but does not need cleanFillCursorForCitationGroup either. */ public Optional getRawCursorForCitationGroup(CitationGroup group, XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { NamedRange namedRange = getNamedRangeOrThrow(group); return namedRange.getRawCursor(doc); } @@ -364,44 +346,46 @@ public Optional getRawCursorForCitationGroup(CitationGroup group, X * Must be followed by call to cleanFillCursorForCitationGroup */ public XTextCursor getFillCursorForCitationGroup(CitationGroup group, XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException, - CreationException { + throws + NoDocumentException, + WrappedTargetException, + CreationException { NamedRange namedRange = getNamedRangeOrThrow(group); return namedRange.getFillCursor(doc); } - /** To be called after getFillCursorForCitationGroup */ + /** + * To be called after getFillCursorForCitationGroup + */ public void cleanFillCursorForCitationGroup(CitationGroup group, XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { NamedRange namedRange = getNamedRangeOrThrow(group); namedRange.cleanFillCursor(doc); } - public List getCitationEntries(XTextDocument doc, CitationGroups cgs) - throws - WrappedTargetException, - NoDocumentException { + public List getCitationEntries(XTextDocument doc, CitationGroups citationGroups) + throws + WrappedTargetException, + NoDocumentException { switch (dataModel) { case JabRef52: // One context per CitationGroup: Backend52 (DataModel.JabRef52) // For DataModel.JabRef60 (Backend60) we need one context per Citation - List citations = new ArrayList<>(cgs.numberOfCitationGroups()); - for (CitationGroup group : cgs.getCitationGroupsUnordered()) { + List citations = new ArrayList<>(citationGroups.numberOfCitationGroups()); + for (CitationGroup group : citationGroups.getCitationGroupsUnordered()) { String name = group.groupId.citationGroupIdAsString(); XTextCursor cursor = (this - .getRawCursorForCitationGroup(group, doc) - .orElseThrow(IllegalStateException::new)); + .getRawCursorForCitationGroup(group, doc) + .orElseThrow(IllegalStateException::new)); String context = GetContext.getCursorStringWithContext(cursor, 30, 30, true); Optional pageInfo = (group.numberOfCitations() > 0 - ? (getPageInfoFromData(group) - .map(e -> OOText.toString(e))) - : Optional.empty()); + ? (getPageInfoFromData(group) + .map(e -> OOText.toString(e))) + : Optional.empty()); CitationEntry entry = new CitationEntry(name, context, pageInfo); citations.add(entry); } @@ -414,15 +398,15 @@ public List getCitationEntries(XTextDocument doc, CitationGroups } } - /* + /** * Only applies to storage. Citation markers are not changed. */ public void applyCitationEntries(XTextDocument doc, List citationEntries) - throws - PropertyVetoException, - IllegalTypeException, - IllegalArgumentException, - WrappedTargetException { + throws + PropertyVetoException, + IllegalTypeException, + IllegalArgumentException, + WrappedTargetException { switch (dataModel) { case JabRef52: @@ -436,12 +420,11 @@ public void applyCitationEntries(XTextDocument doc, List citation } break; case JabRef60: - // xx + // ToDo: Implement throw new IllegalStateException("applyCitationEntries for JabRef60 is not implemented yet"); default: throw new IllegalStateException("applyCitationEntries: unhandled dataModel "); } } - } diff --git a/src/main/java/org/jabref/logic/openoffice/backend/Codec52.java b/src/main/java/org/jabref/logic/openoffice/backend/Codec52.java index 45f3bd3ca04..0b8932fda6c 100644 --- a/src/main/java/org/jabref/logic/openoffice/backend/Codec52.java +++ b/src/main/java/org/jabref/logic/openoffice/backend/Codec52.java @@ -12,30 +12,35 @@ import org.jabref.model.openoffice.style.CitationType; /** - * How and what is encoded in reference mark names under JabRef 5.2. - * - * - pageInfo does not appear here. It is not encoded in the mark name. + * How and what is encoded in reference mark names under JabRef 5.2. + *

+ * - pageInfo does not appear here. It is not encoded in the mark name. */ class Codec52 { private static final String BIB_CITATION = "JR_cite"; private static final Pattern CITE_PATTERN = - // Pattern.compile(BIB_CITATION + "(\\d*)_(\\d*)_(.*)"); - // citationType is always "1" "2" or "3" - Pattern.compile(BIB_CITATION + "(\\d*)_([123])_(.*)"); + // Pattern.compile(BIB_CITATION + "(\\d*)_(\\d*)_(.*)"); + // citationType is always "1" "2" or "3" + Pattern.compile(BIB_CITATION + "(\\d*)_([123])_(.*)"); private Codec52() { - /**/ } /** * This is what we get back from parsing a refMarkName. */ public static class ParsedMarkName { - /** "", "0", "1" ... */ + /** + * "", "0", "1" ... + */ public final String index; - /** in-text-citation type */ + /** + * in-text-citation type + */ public final CitationType citationType; - /** Citation keys embedded in the reference mark. */ + /** + * Citation keys embedded in the reference mark. + */ public final List citationKeys; ParsedMarkName(String index, CitationType citationType, List citationKeys) { @@ -64,21 +69,19 @@ private static int citationTypeToInt(CitationType type) { case AUTHORYEAR_PAR -> 1; case AUTHORYEAR_INTEXT -> 2; case INVISIBLE_CIT -> 3; - default -> throw new IllegalArgumentException("Invalid CitationType"); }; } /** - * Produce a reference mark name for JabRef for the given citationType and list citation keys that - * does not yet appear among the reference marks of the document. + * Produce a reference mark name for JabRef for the given citationType and list citation keys that does not yet appear among the reference marks of the document. * * @param usedNames Reference mark names already in use. * @param citationKeys Identifies the cited sources. * @param citationType Encodes the effect of withText and inParenthesis options. - * - * The first occurrence of citationKeys gets no serial number, the second gets 0, the third 1 ... - * - * Or the first unused in this series, after removals. + *

+ * The first occurrence of citationKeys gets no serial number, the second gets 0, the third 1 ... + *

+ * Or the first unused in this series, after removals. */ public static String getUniqueMarkName(Set usedNames, List citationKeys, @@ -100,7 +103,6 @@ public static String getUniqueMarkName(Set usedNames, * Parse a JabRef (reference) mark name. * * @return Optional.empty() on failure. - * */ public static Optional parseMarkName(String refMarkName) { @@ -117,8 +119,7 @@ public static Optional parseMarkName(String refMarkName) { } /** - * @return true if name matches the pattern used for JabRef - * reference mark names. + * @return true if name matches the pattern used for JabRef reference mark names. */ public static boolean isJabRefReferenceMarkName(String name) { return (CITE_PATTERN.matcher(name).find()); diff --git a/src/main/java/org/jabref/logic/openoffice/backend/GetContext.java b/src/main/java/org/jabref/logic/openoffice/backend/GetContext.java index ffce5b9553e..865682f0ccc 100644 --- a/src/main/java/org/jabref/logic/openoffice/backend/GetContext.java +++ b/src/main/java/org/jabref/logic/openoffice/backend/GetContext.java @@ -12,21 +12,17 @@ public class GetContext { private static final Logger LOGGER = LoggerFactory.getLogger(GetContext.class); private GetContext() { - // Just to hide the public constructor } /** - * Get the text belonging to cursor with up to - * charBefore and charAfter characters of context. - * - * The actual context may be smaller than requested. - * - * @param cursor - * @param charBefore Number of characters requested. - * @param charAfter Number of characters requested. - * @param htmlMarkup If true, the text belonging to the reference mark is surrounded by bold - * html tag. + * Get the text belonging to cursor with up to charBefore and charAfter characters of context. + *

+ * The actual context may be smaller than requested. * + * @param cursor + * @param charBefore Number of characters requested. + * @param charAfter Number of characters requested. + * @param htmlMarkup If true, the text belonging to the reference mark is surrounded by bold html tag. */ public static String getCursorStringWithContext(XTextCursor cursor, int charBefore, @@ -43,7 +39,7 @@ public static String getCursorStringWithContext(XTextCursor cursor, // If we are close to charBefore and see a space, then cut here. Might avoid cutting // a word in half. if ((i >= (charBefore - flex)) - && Character.isWhitespace(cursor.getString().charAt(0))) { + && Character.isWhitespace(cursor.getString().charAt(0))) { break; } } catch (IndexOutOfBoundsException ex) { @@ -72,10 +68,9 @@ public static String getCursorStringWithContext(XTextCursor cursor, String result = cursor.getString(); if (htmlMarkup) { result = (result.substring(0, addedBefore) - + "" + citPart + "" - + result.substring(lengthWithBefore)); + + "" + citPart + "" + + result.substring(lengthWithBefore)); } return result.trim(); } - } diff --git a/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeManagerReferenceMark.java b/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeManagerReferenceMark.java index 93ad6d12908..fead1169b8e 100644 --- a/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeManagerReferenceMark.java +++ b/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeManagerReferenceMark.java @@ -21,23 +21,23 @@ public NamedRange createNamedRange(XTextDocument doc, XTextCursor position, boolean insertSpaceAfter, boolean withoutBrackets) - throws - CreationException { + throws + CreationException { return NamedRangeReferenceMark.create(doc, refMarkName, position, insertSpaceAfter, withoutBrackets); } @Override public List getUsedNames(XTextDocument doc) - throws - NoDocumentException { + throws + NoDocumentException { return UnoReferenceMark.getListOfNames(doc); } @Override public Optional getNamedRangeFromDocument(XTextDocument doc, String refMarkName) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { return (NamedRangeReferenceMark .getFromDocument(doc, refMarkName) .map(x -> x)); diff --git a/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java b/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java index 324b984377c..e816b3bfa97 100644 --- a/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java +++ b/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java @@ -17,25 +17,26 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -// was StorageBaseRefMark - class NamedRangeReferenceMark implements NamedRange { private static final String ZERO_WIDTH_SPACE = "\u200b"; // for debugging we may want visible bracket private static final boolean - REFERENCE_MARK_USE_INVISIBLE_BRACKETS = true; // !debug; + REFERENCE_MARK_USE_INVISIBLE_BRACKETS = true; // !debug; public static final String - REFERENCE_MARK_LEFT_BRACKET = REFERENCE_MARK_USE_INVISIBLE_BRACKETS ? ZERO_WIDTH_SPACE : "<"; + REFERENCE_MARK_LEFT_BRACKET = REFERENCE_MARK_USE_INVISIBLE_BRACKETS ? ZERO_WIDTH_SPACE : "<"; public static final String - REFERENCE_MARK_RIGHT_BRACKET = REFERENCE_MARK_USE_INVISIBLE_BRACKETS ? ZERO_WIDTH_SPACE : ">"; + REFERENCE_MARK_RIGHT_BRACKET = REFERENCE_MARK_USE_INVISIBLE_BRACKETS ? ZERO_WIDTH_SPACE : ">"; private static final Logger LOGGER = LoggerFactory.getLogger(NamedRangeReferenceMark.class); - private String rangeId; /* reference mark name */ + /** + * reference mark name + */ + private final String rangeId; private NamedRangeReferenceMark(String rangeId) { this.rangeId = rangeId; @@ -46,20 +47,15 @@ String getId() { } /** - * Insert {@code n} spaces in a way that reference marks just before or just after the cursor - * are not affected. - * - * This is based on the observation, that starting two new paragraphs separates us from - * reference marks on either side. - * - * The pattern used is: - * {@code safeInsertSpaces(n): para, para, left, space(n), right-delete, left(n), left-delete} - * - * @param position Where to insert (at position.getStart()) - * @param numSpaces Number of spaces to insert. - * - * @return a new cursor, covering the just-inserted spaces. + * Insert {@code n} spaces in a way that reference marks just before or just after the cursor are not affected. + *

+ * This is based on the observation, that starting two new paragraphs separates us from reference marks on either side. + *

+ * The pattern used is: {@code safeInsertSpaces(n): para, para, left, space(n), right-delete, left(n), left-delete} * + * @param position Where to insert (at position.getStart()) + * @param numSpaces Number of spaces to insert. + * @return a new cursor, covering the just-inserted spaces. */ private static XTextCursor safeInsertSpacesBetweenReferenceMarks(XTextRange position, int numSpaces) { // Start with an empty cursor at position.getStart(); @@ -82,8 +78,8 @@ private static void createReprInDocument(XTextDocument doc, XTextCursor position, boolean insertSpaceAfter, boolean withoutBrackets) - throws - CreationException { + throws + CreationException { // The cursor we received: we push it before us. position.collapseToEnd(); @@ -101,8 +97,8 @@ private static void createReprInDocument(XTextDocument doc, final String left = NamedRangeReferenceMark.REFERENCE_MARK_LEFT_BRACKET; final String right = NamedRangeReferenceMark.REFERENCE_MARK_RIGHT_BRACKET; String bracketedContent = (withoutBrackets - ? "" - : left + right); + ? "" + : left + right); cursor.getText().insertString(cursor, bracketedContent, true); @@ -123,8 +119,8 @@ static NamedRangeReferenceMark create(XTextDocument doc, XTextCursor position, boolean insertSpaceAfter, boolean withoutBrackets) - throws - CreationException { + throws + CreationException { createReprInDocument(doc, refMarkName, position, insertSpaceAfter, withoutBrackets); return new NamedRangeReferenceMark(refMarkName); @@ -134,23 +130,23 @@ static NamedRangeReferenceMark create(XTextDocument doc, * @return Optional.empty if there is no corresponding range. */ static Optional getFromDocument(XTextDocument doc, String refMarkName) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { return (UnoReferenceMark.getAnchor(doc, refMarkName) - .map(e -> new NamedRangeReferenceMark(refMarkName))); + .map(e -> new NamedRangeReferenceMark(refMarkName))); } /** * Remove it from the document. - * + *

* See: removeCitationGroups */ @Override public void removeFromDocument(XTextDocument doc) - throws - WrappedTargetException, - NoDocumentException { + throws + WrappedTargetException, + NoDocumentException { UnoReferenceMark.removeIfExists(doc, this.getRangeName()); } @@ -160,34 +156,27 @@ public String getRangeName() { } /** - * - * @return Optional.empty if the reference mark is missing. - * - * See: UnoReferenceMark.getAnchor + * @return Optional.empty if the reference mark is missing. See: UnoReferenceMark.getAnchor */ @Override public Optional getMarkRange(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { String name = this.getRangeName(); return UnoReferenceMark.getAnchor(doc, name); } /** - * Cursor for the reference marks as is, not prepared for filling, but does not need - * cleanFillCursor either. - * - * @return Optional.empty() if reference mark is missing from the document, - * otherwise an XTextCursor for getMarkRange + * Cursor for the reference marks as is, not prepared for filling, but does not need cleanFillCursor either. * - * See: getRawCursorForCitationGroup + * @return Optional.empty() if reference mark is missing from the document, otherwise an XTextCursor for getMarkRange See: getRawCursorForCitationGroup */ @Override public Optional getRawCursor(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { String name = this.getRangeName(); @@ -210,10 +199,10 @@ public Optional getRawCursor(XTextDocument doc) */ @Override public XTextCursor getFillCursor(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException, - CreationException { + throws + NoDocumentException, + WrappedTargetException, + CreationException { String name = this.getRangeName(); @@ -357,8 +346,8 @@ public static void checkFillCursor(XTextCursor cursor) { alpha.goLeft(leftLength, true); if (!left.equals(alpha.getString())) { String msg = String.format("checkFillCursor:" - + " ('%s') is not prefixed with REFERENCE_MARK_LEFT_BRACKET, has '%s'", - cursor.getString(), alpha.getString()); + + " ('%s') is not prefixed with REFERENCE_MARK_LEFT_BRACKET, has '%s'", + cursor.getString(), alpha.getString()); throw new IllegalStateException(msg); } } @@ -369,24 +358,23 @@ public static void checkFillCursor(XTextCursor cursor) { omega.goRight(rightLength, true); if (!right.equals(omega.getString())) { String msg = String.format("checkFillCursor:" - + " ('%s') is not followed by REFERENCE_MARK_RIGHT_BRACKET, has '%s'", - cursor.getString(), omega.getString()); + + " ('%s') is not followed by REFERENCE_MARK_RIGHT_BRACKET, has '%s'", + cursor.getString(), omega.getString()); throw new IllegalStateException(msg); } } } /** - * Remove brackets, but if the result would become empty, leave them; if the result would be a - * single characer, leave the left bracket. - * + * Remove brackets, but if the result would become empty, leave them; if the result would be a single characer, leave the left bracket. + *

* See: cleanFillCursorForCitationGroup */ @Override public void cleanFillCursor(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { // alwaysRemoveBrackets : full compatibility with JabRef 5.2: brackets are temporary, only // exist between getFillCursor and cleanFillCursor. @@ -422,12 +410,12 @@ public void cleanFillCursor(XTextDocument doc) } boolean removeRight = ((contentLength >= 1) - || ((contentLength == 0) && removeBracketsFromEmpty) - || alwaysRemoveBrackets); + || ((contentLength == 0) && removeBracketsFromEmpty) + || alwaysRemoveBrackets); boolean removeLeft = ((contentLength >= 2) - || ((contentLength == 0) && removeBracketsFromEmpty) - || alwaysRemoveBrackets); + || ((contentLength == 0) && removeBracketsFromEmpty) + || alwaysRemoveBrackets); if (removeRight) { XTextCursor omega = full.getText().createTextCursorByRange(full); diff --git a/src/main/java/org/jabref/logic/openoffice/frontend/OOFrontend.java b/src/main/java/org/jabref/logic/openoffice/frontend/OOFrontend.java index 58057c382e7..01470867257 100644 --- a/src/main/java/org/jabref/logic/openoffice/frontend/OOFrontend.java +++ b/src/main/java/org/jabref/logic/openoffice/frontend/OOFrontend.java @@ -47,9 +47,9 @@ public class OOFrontend { public final CitationGroups citationGroups; public OOFrontend(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { // TODO: dataModel should come from looking at the document and preferences. this.backend = new Backend52(); @@ -58,7 +58,7 @@ public OOFrontend(XTextDocument doc) List citationGroupNames = this.backend.getJabRefReferenceMarkNames(doc); Map citationGroups = - readCitationGroupsFromDocument(this.backend, doc, citationGroupNames); + readCitationGroupsFromDocument(this.backend, doc, citationGroupNames); this.citationGroups = new CitationGroups(citationGroups); } @@ -67,8 +67,8 @@ public OODataModel getDataModel() { } public Optional healthReport(XTextDocument doc) - throws - NoDocumentException { + throws + NoDocumentException { return backend.healthReport(doc); } @@ -76,9 +76,9 @@ public Optional healthReport(XTextDocument doc) readCitationGroupsFromDocument(Backend52 backend, XTextDocument doc, List citationGroupNames) - throws - WrappedTargetException, - NoDocumentException { + throws + WrappedTargetException, + NoDocumentException { Map citationGroups = new HashMap<>(); for (String name : citationGroupNames) { @@ -89,32 +89,27 @@ public Optional healthReport(XTextDocument doc) } /** - * Creates a list of {@code RangeSortable} values for our {@code CitationGroup} - * values. Originally designed to be passed to {@code visualSort}. - * + * Creates a list of {@code RangeSortable} values for our {@code CitationGroup} values. Originally designed to be passed to {@code visualSort}. + *

* The elements of the returned list are actually of type {@code RangeSortEntry}. + *

+ * The result is sorted within {@code XTextRange.getText()} partitions of the citation groups according to their {@code XTextRange} (before mapping to footnote marks). + *

+ * In the result, RangeSortable.getIndexInPosition() contains unique indexes within the original partition (not after mapFootnotesToFootnoteMarks). * - * The result is sorted within {@code XTextRange.getText()} partitions of the citation groups - * according to their {@code XTextRange} (before mapping to footnote marks). - * - * In the result, RangeSortable.getIndexInPosition() contains unique indexes within the original - * partition (not after mapFootnotesToFootnoteMarks). - * - * @param mapFootnotesToFootnoteMarks If true, replace ranges in footnotes with the range of the - * corresponding footnote mark. This is used for numbering the citations. - * + * @param mapFootnotesToFootnoteMarks If true, replace ranges in footnotes with the range of the corresponding footnote mark. This is used for numbering the citations. */ - private List> - createVisualSortInput(XTextDocument doc, boolean mapFootnotesToFootnoteMarks) - throws - NoDocumentException, - WrappedTargetException { + private List> createVisualSortInput(XTextDocument doc, + boolean mapFootnotesToFootnoteMarks) + throws + NoDocumentException, + WrappedTargetException { List> sortables = new ArrayList<>(); for (CitationGroup group : citationGroups.getCitationGroupsUnordered()) { XTextRange range = (this - .getMarkRange(doc, group) - .orElseThrow(IllegalStateException::new)); + .getMarkRange(doc, group) + .orElseThrow(IllegalStateException::new)); sortables.add(new RangeSortEntry<>(range, 0, group)); } @@ -137,7 +132,7 @@ public Optional healthReport(XTextDocument doc) // Sort within partitions RangeSort.RangePartitions> partitions = - RangeSort.partitionAndSortRanges(sortables); + RangeSort.partitionAndSortRanges(sortables); // build final list List> result = new ArrayList<>(); @@ -148,75 +143,64 @@ public Optional healthReport(XTextDocument doc) sortable.setIndexInPosition(indexInPartition++); if (mapFootnotesToFootnoteMarks) { Optional footnoteMarkRange = - UnoTextRange.getFootnoteMarkRange(sortable.getRange()); + UnoTextRange.getFootnoteMarkRange(sortable.getRange()); // Adjust range if we are inside a footnote: - if (footnoteMarkRange.isPresent()) { - sortable.setRange(footnoteMarkRange.get()); - } + footnoteMarkRange.ifPresent(sortable::setRange); } result.add(sortable); } } - return result.stream().map(e -> e).collect(Collectors.toList()); + return new ArrayList<>(result); } /** - * @param mapFootnotesToFootnoteMarks If true, sort reference marks in footnotes as if they - * appeared at the corresponding footnote mark. - * - * @return citation groups sorted by their visual positions. - * - * Limitation: for two column layout visual (top-down, left-right) order does not match the - * expected (textual) order. - * + * @param mapFootnotesToFootnoteMarks If true, sort reference marks in footnotes as if they appeared at the corresponding footnote mark. + * @return citation groups sorted by their visual positions. Limitation: for two column layout visual (top-down, left-right) order does not match the expected (textual) order. */ private List getVisuallySortedCitationGroups(XTextDocument doc, boolean mapFootnotesToFootnoteMarks, FunctionalTextViewCursor fcursor) - throws - WrappedTargetException, - NoDocumentException { + throws + WrappedTargetException, + NoDocumentException { List> sortables = createVisualSortInput(doc, mapFootnotesToFootnoteMarks); List> sorted = RangeSortVisual.visualSort(sortables, doc, fcursor); return (sorted.stream() - .map(RangeSortable::getContent) - .collect(Collectors.toList())); + .map(RangeSortable::getContent) + .collect(Collectors.toList())); } /** * Return citation groups in visual order within (but not across) XText partitions. - * - * This is (1) sufficient for combineCiteMarkers which looks for consecutive XTextRanges within - * each XText, (2) not confused by multicolumn layout or multipage display. + *

+ * This is (1) sufficient for combineCiteMarkers which looks for consecutive XTextRanges within each XText, (2) not confused by multicolumn layout or multipage display. */ public List getCitationGroupsSortedWithinPartitions(XTextDocument doc, boolean mapFootnotesToFootnoteMarks) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { // This is like getVisuallySortedCitationGroups, // but we skip the visualSort part. List> sortables = - createVisualSortInput(doc, mapFootnotesToFootnoteMarks); + createVisualSortInput(doc, mapFootnotesToFootnoteMarks); - return (sortables.stream().map(e -> e.getContent()).collect(Collectors.toList())); + return (sortables.stream().map(RangeSortable::getContent).collect(Collectors.toList())); } /** - * Create a citation group for the given citation keys, at the end of position. - * - * On return {@code position} is collapsed, and is after the inserted space, or at the end of - * the reference mark. + * Create a citation group for the given citation keys, at the end of position. + *

+ * On return {@code position} is collapsed, and is after the inserted space, or at the end of the reference mark. * - * @param citationKeys In storage order - * @param pageInfos In storage order + * @param citationKeys In storage order + * @param pageInfos In storage order * @param citationType - * @param position Collapsed to its end. - * @param insertSpaceAfter If true, we insert a space after the mark, that carries on format of - * characters from the original position. + * @param position Collapsed to its end. + * @param insertSpaceAfter If true, we insert a space after the mark, that carries on format of characters from the original position. */ public CitationGroup createCitationGroup(XTextDocument doc, List citationKeys, @@ -224,24 +208,24 @@ public CitationGroup createCitationGroup(XTextDocument doc, CitationType citationType, XTextCursor position, boolean insertSpaceAfter) - throws - CreationException, - NoDocumentException, - WrappedTargetException, - NotRemoveableException, - PropertyVetoException, - IllegalTypeException { + throws + CreationException, + NoDocumentException, + WrappedTargetException, + NotRemoveableException, + PropertyVetoException, + IllegalTypeException { Objects.requireNonNull(pageInfos); if (pageInfos.size() != citationKeys.size()) { throw new IllegalArgumentException("pageInfos.size != citationKeys.size"); } CitationGroup group = backend.createCitationGroup(doc, - citationKeys, - pageInfos, - citationType, - position, - insertSpaceAfter); + citationKeys, + pageInfos, + citationType, + position, + insertSpaceAfter); this.citationGroups.afterCreateCitationGroup(group); return group; @@ -251,22 +235,22 @@ public CitationGroup createCitationGroup(XTextDocument doc, * Remove {@code group} both from the document and notify {@code citationGroups} */ public void removeCitationGroup(CitationGroup group, XTextDocument doc) - throws - WrappedTargetException, - NoDocumentException, - NotRemoveableException { + throws + WrappedTargetException, + NoDocumentException, + NotRemoveableException { backend.removeCitationGroup(group, doc); this.citationGroups.afterRemoveCitationGroup(group); } - public void removeCitationGroups(List cgs, XTextDocument doc) - throws - WrappedTargetException, - NoDocumentException, - NotRemoveableException { + public void removeCitationGroups(List citationGroups, XTextDocument doc) + throws + WrappedTargetException, + NoDocumentException, + NotRemoveableException { - for (CitationGroup group : cgs) { + for (CitationGroup group : citationGroups) { removeCitationGroup(group, doc); } } @@ -275,20 +259,19 @@ public void removeCitationGroups(List cgs, XTextDocument doc) * ranges controlled by citation groups should not overlap with each other. * * @return Optional.empty() if the reference mark is missing. - * */ public Optional getMarkRange(XTextDocument doc, CitationGroup group) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { return backend.getMarkRange(group, doc); } public XTextCursor getFillCursorForCitationGroup(XTextDocument doc, CitationGroup group) - throws - NoDocumentException, - WrappedTargetException, - CreationException { + throws + NoDocumentException, + WrappedTargetException, + CreationException { return backend.getFillCursorForCitationGroup(group, doc); } @@ -296,41 +279,39 @@ public XTextCursor getFillCursorForCitationGroup(XTextDocument doc, CitationGrou * Remove brackets added by getFillCursorForCitationGroup. */ public void cleanFillCursorForCitationGroup(XTextDocument doc, CitationGroup group) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { backend.cleanFillCursorForCitationGroup(group, doc); } /** - * @return A RangeForOverlapCheck for each citation group. - * - * result.size() == nRefMarks + * @return A RangeForOverlapCheck for each citation group. result.size() == nRefMarks */ public List> citationRanges(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { List> result = - new ArrayList<>(citationGroups.numberOfCitationGroups()); + new ArrayList<>(citationGroups.numberOfCitationGroups()); for (CitationGroup group : citationGroups.getCitationGroupsUnordered()) { XTextRange range = this.getMarkRange(doc, group).orElseThrow(IllegalStateException::new); String description = group.groupId.citationGroupIdAsString(); result.add(new RangeForOverlapCheck<>(range, - group.groupId, - RangeForOverlapCheck.REFERENCE_MARK_KIND, - description)); + group.groupId, + RangeForOverlapCheck.REFERENCE_MARK_KIND, + description)); } return result; } public List> bibliographyRanges(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { List> result = new ArrayList<>(); @@ -338,9 +319,9 @@ public List> bibliographyRanges(XTextDocum if (range.isPresent()) { String description = "bibliography"; result.add(new RangeForOverlapCheck<>(range.get(), - new CitationGroupId("bibliography"), - RangeForOverlapCheck.BIBLIOGRAPHY_MARK_KIND, - description)); + new CitationGroupId("bibliography"), + RangeForOverlapCheck.BIBLIOGRAPHY_MARK_KIND, + description)); } return result; } @@ -353,23 +334,17 @@ public List> viewCursorRanges(XTextDocumen if (range.isPresent()) { String description = "cursor"; result.add(new RangeForOverlapCheck<>(range.get(), - new CitationGroupId("cursor"), - RangeForOverlapCheck.CURSOR_MARK_KIND, - description)); + new CitationGroupId("cursor"), + RangeForOverlapCheck.CURSOR_MARK_KIND, + description)); } return result; } /** - * @return A range for each footnote mark where the footnote contains at least one citation group. - * - * Purpose: We do not want markers of footnotes containing reference marks to overlap with - * reference marks. Overwriting these footnote marks might kill our reference marks in the - * footnote. - * - * Note: Here we directly communicate to the document, not through the backend. This is because - * mapping ranges to footnote marks does not depend on how do we mark or structure those - * ranges. + * @return A range for each footnote mark where the footnote contains at least one citation group. Purpose: We do not want markers of footnotes containing reference marks to overlap with reference marks. Overwriting these footnote marks might kill our reference marks in the footnote. + *

+ * Note: Here we directly communicate to the document, not through the backend. This is because mapping ranges to footnote marks does not depend on how do we mark or structure those ranges. */ public List> footnoteMarkRanges(XTextDocument doc, List> citationRanges) { @@ -379,7 +354,7 @@ public List> viewCursorRanges(XTextDocumen List> result = new ArrayList<>(); RangeSort.RangePartitions> partitions = - RangeSort.partitionRanges(citationRanges); + RangeSort.partitionRanges(citationRanges); // Each partition corresponds to an XText, and each footnote has a single XText. // (This latter ignores the possibility of XTextContents inserted into footnotes.) @@ -398,9 +373,9 @@ public List> viewCursorRanges(XTextDocumen } result.add(new RangeForOverlapCheck<>(footnoteMarkRange.get(), - citationRange.idWithinKind, - RangeForOverlapCheck.FOOTNOTE_MARK_KIND, - "FootnoteMark for " + citationRange.format())); + citationRange.idWithinKind, + RangeForOverlapCheck.FOOTNOTE_MARK_KIND, + "FootnoteMark for " + citationRange.format())); } return result; } @@ -414,14 +389,14 @@ static String rangeOverlapsToMessage(List> overlap : overlaps) { String listOfRanges = (overlap.valuesForOverlappingRanges.stream() - .map(v -> String.format("'%s'", v.format())) - .collect(Collectors.joining(", "))); + .map(v -> String.format("'%s'", v.format())) + .collect(Collectors.joining(", "))); msg.append( - switch (overlap.kind) { - case EQUAL_RANGE -> Localization.lang("Found identical ranges"); - case OVERLAP -> Localization.lang("Found overlapping ranges"); - case TOUCH -> Localization.lang("Found touching ranges"); - }); + switch (overlap.kind) { + case EQUAL_RANGE -> Localization.lang("Found identical ranges"); + case OVERLAP -> Localization.lang("Found overlapping ranges"); + case TOUCH -> Localization.lang("Found touching ranges"); + }); msg.append(": "); msg.append(listOfRanges); msg.append("\n"); @@ -431,18 +406,18 @@ static String rangeOverlapsToMessage(List * Assume userRanges is small (usually 1 elements for checking the cursor) - * + *

* Returns on first problem found. */ public OOVoidResult checkRangeOverlapsWithCursor(XTextDocument doc, List> userRanges, boolean requireSeparation) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { List> citationRanges = citationRanges(doc); List> ranges = new ArrayList<>(); @@ -453,30 +428,29 @@ static String rangeOverlapsToMessage(List>> overlaps = - RangeOverlapBetween.findFirst(doc, - userRanges, - ranges, - requireSeparation); + RangeOverlapBetween.findFirst(doc, + userRanges, + ranges, + requireSeparation); if (overlaps.isEmpty()) { return OOVoidResult.ok(); } return OOVoidResult.error(new JabRefException("Found overlapping or touching ranges", - rangeOverlapsToMessage(overlaps))); + rangeOverlapsToMessage(overlaps))); } /** * @param requireSeparation Report range pairs that only share a boundary. - * @param reportAtMost Limit number of overlaps reported (0 for no limit) - * + * @param reportAtMost Limit number of overlaps reported (0 for no limit) */ public OOVoidResult checkRangeOverlaps(XTextDocument doc, List> userRanges, boolean requireSeparation, int reportAtMost) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { List> citationRanges = citationRanges(doc); List> ranges = new ArrayList<>(); @@ -486,69 +460,56 @@ public OOVoidResult checkRangeOverlaps(XTextDocument doc, ranges.addAll(footnoteMarkRanges(doc, citationRanges)); List>> overlaps = - RangeOverlapWithin.findOverlappingRanges(doc, ranges, requireSeparation, reportAtMost); + RangeOverlapWithin.findOverlappingRanges(doc, ranges, requireSeparation, reportAtMost); if (overlaps.isEmpty()) { return OOVoidResult.ok(); } return OOVoidResult.error(new JabRefException("Found overlapping or touching ranges", - rangeOverlapsToMessage(overlaps))); + rangeOverlapsToMessage(overlaps))); } /** - * GUI: Get a list of CitationEntry objects corresponding to citations - * in the document. - * + * GUI: Get a list of CitationEntry objects corresponding to citations in the document. + *

* Called from: ManageCitationsDialogViewModel constructor. * - * @return A list with entries corresponding to citations in the text, in arbitrary order (same - * order as from getJabRefReferenceMarkNames). - * - * Note: visual or alphabetic order could be more manageable for the user. We - * could provide these here, but switching between them needs change on GUI - * (adding a toggle or selector). - * - * Note: CitationEntry implements Comparable, where compareTo() and equals() are - * based on refMarkName. The order used in the "Manage citations" dialog - * does not seem to use that. - * - * The 1st is labeled "Citation" (show citation in bold, and some context - * around it). - * - * The columns can be sorted by clicking on the column title. For the - * "Citation" column, the sorting is based on the content, (the context - * before the citation), not on the citation itself. - * - * In the "Extra information ..." column some visual indication of the - * editable part could be helpful. - * - * Wish: selecting an entry (or a button in the line) in the GUI could move the cursor - * in the document to the entry. + * @return A list with entries corresponding to citations in the text, in arbitrary order (same order as from getJabRefReferenceMarkNames). Note: visual or alphabetic order could be more manageable for the user. We could provide these here, but switching between them needs change on GUI (adding a toggle or selector). + *

+ * Note: CitationEntry implements Comparable, where compareTo() and equals() are based on refMarkName. The order used in the "Manage citations" dialog does not seem to use that. + *

+ * The 1st is labeled "Citation" (show citation in bold, and some context around it). + *

+ * The columns can be sorted by clicking on the column title. For the "Citation" column, the sorting is based on the content, (the context before the citation), not on the citation itself. + *

+ * In the "Extra information ..." column some visual indication of the editable part could be helpful. + *

+ * Wish: selecting an entry (or a button in the line) in the GUI could move the cursor in the document to the entry. */ public List getCitationEntries(XTextDocument doc) - throws - WrappedTargetException, - NoDocumentException { + throws + WrappedTargetException, + NoDocumentException { return this.backend.getCitationEntries(doc, citationGroups); } public void applyCitationEntries(XTextDocument doc, List citationEntries) - throws - PropertyVetoException, - IllegalTypeException, - IllegalArgumentException, - WrappedTargetException { + throws + PropertyVetoException, + IllegalTypeException, + IllegalArgumentException, + WrappedTargetException { this.backend.applyCitationEntries(doc, citationEntries); } public void imposeGlobalOrder(XTextDocument doc, FunctionalTextViewCursor fcursor) - throws - WrappedTargetException, - NoDocumentException { + throws + WrappedTargetException, + NoDocumentException { boolean mapFootnotesToFootnoteMarks = true; List sortedCitationGroups = - getVisuallySortedCitationGroups(doc, mapFootnotesToFootnoteMarks, fcursor); + getVisuallySortedCitationGroups(doc, mapFootnotesToFootnoteMarks, fcursor); List sortedCitationGroupIds = OOListUtil.map(sortedCitationGroups, group -> group.groupId); citationGroups.setGlobalOrder(sortedCitationGroupIds); } diff --git a/src/main/java/org/jabref/logic/openoffice/frontend/RangeForOverlapCheck.java b/src/main/java/org/jabref/logic/openoffice/frontend/RangeForOverlapCheck.java index 6cbf560ec62..914195bd243 100644 --- a/src/main/java/org/jabref/logic/openoffice/frontend/RangeForOverlapCheck.java +++ b/src/main/java/org/jabref/logic/openoffice/frontend/RangeForOverlapCheck.java @@ -6,17 +6,10 @@ /** * Describe a protected range for overlap checking and reporting. - * - * To check that our protected ranges do not overlap, we collect - * these ranges. To check for overlaps between these, we need the - * {@code range} itself. To report the results of overlap - * checking, we need a {@code description} that can be understood - * by the user. - * - * To be able to refer back to more extended data, we might need to - * identify its {@code kind}, and its index in the corresponding - * tables or other identifier within its kind ({@code idWithinKind}) - * + *

+ * To check that our protected ranges do not overlap, we collect these ranges. To check for overlaps between these, we need the {@code range} itself. To report the results of overlap checking, we need a {@code description} that can be understood by the user. + *

+ * To be able to refer back to more extended data, we might need to identify its {@code kind}, and its index in the corresponding tables or other identifier within its kind ({@code idWithinKind}) */ public class RangeForOverlapCheck implements RangeHolder { diff --git a/src/main/java/org/jabref/logic/openoffice/frontend/UpdateBibliography.java b/src/main/java/org/jabref/logic/openoffice/frontend/UpdateBibliography.java index ebda8359ba5..99e71e13725 100644 --- a/src/main/java/org/jabref/logic/openoffice/frontend/UpdateBibliography.java +++ b/src/main/java/org/jabref/logic/openoffice/frontend/UpdateBibliography.java @@ -17,22 +17,18 @@ import com.sun.star.text.XTextDocument; import com.sun.star.text.XTextRange; -/* - * Update document: citation marks and bibliography - */ public class UpdateBibliography { private static final String BIB_SECTION_NAME = "JR_bib"; private static final String BIB_SECTION_END_NAME = "JR_bib_end"; private UpdateBibliography() { - /**/ } public static Optional getBibliographyRange(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { return UnoTextSection.getAnchor(doc, BIB_SECTION_NAME); } @@ -44,28 +40,28 @@ public static void rebuildBibTextSection(XTextDocument doc, CitedKeys bibliography, OOBibStyle style, boolean alwaysAddCitedOnPages) - throws - WrappedTargetException, - CreationException, - NoDocumentException { + throws + WrappedTargetException, + CreationException, + NoDocumentException { clearBibTextSectionContent2(doc); populateBibTextSection(doc, - frontend, - bibliography, - style, - alwaysAddCitedOnPages); + frontend, + bibliography, + style, + alwaysAddCitedOnPages); } /** * Insert a paragraph break and create a text section for the bibliography. - * + *

* Only called from `clearBibTextSectionContent2` */ private static void createBibTextSection2(XTextDocument doc) - throws - CreationException { + throws + CreationException { // Always creating at the end of the document. // Alternatively, we could receive a cursor. @@ -75,23 +71,20 @@ private static void createBibTextSection2(XTextDocument doc) } /** - * Find and clear the text section BIB_SECTION_NAME to "", - * or create it. - * + * Find and clear the text section BIB_SECTION_NAME to "", or create it. + *

* Only called from: `rebuildBibTextSection` - * */ private static void clearBibTextSectionContent2(XTextDocument doc) - throws - CreationException, - NoDocumentException, - WrappedTargetException { + throws + CreationException, + NoDocumentException, + WrappedTargetException { // Optional sectionRange = UnoTextSection.getAnchor(doc, BIB_SECTION_NAME); Optional sectionRange = getBibliographyRange(doc); if (sectionRange.isEmpty()) { createBibTextSection2(doc); - return; } else { // Clear it XTextCursor cursor = doc.getText().createTextCursorByRange(sectionRange.get()); @@ -101,7 +94,7 @@ private static void clearBibTextSectionContent2(XTextDocument doc) /** * Only called from: `rebuildBibTextSection` - * + *

* Assumes the section named BIB_SECTION_NAME exists. */ private static void populateBibTextSection(XTextDocument doc, @@ -109,11 +102,11 @@ private static void populateBibTextSection(XTextDocument doc, CitedKeys bibliography, OOBibStyle style, boolean alwaysAddCitedOnPages) - throws - CreationException, - IllegalArgumentException, - NoDocumentException, - WrappedTargetException { + throws + CreationException, + IllegalArgumentException, + NoDocumentException, + WrappedTargetException { XTextRange sectionRange = getBibliographyRange(doc).orElseThrow(IllegalStateException::new); @@ -122,9 +115,9 @@ private static void populateBibTextSection(XTextDocument doc, // emit the title of the bibliography OOTextIntoOO.removeDirectFormatting(cursor); OOText bibliographyText = OOFormatBibliography.formatBibliography(frontend.citationGroups, - bibliography, - style, - alwaysAddCitedOnPages); + bibliography, + style, + alwaysAddCitedOnPages); OOTextIntoOO.write(doc, cursor, bibliographyText); cursor.collapseToEnd(); @@ -140,5 +133,4 @@ private static void populateBibTextSection(XTextDocument doc, cursor.collapseToEnd(); } - } diff --git a/src/main/java/org/jabref/logic/openoffice/frontend/UpdateCitationMarkers.java b/src/main/java/org/jabref/logic/openoffice/frontend/UpdateCitationMarkers.java index 834fce1d0ce..d61e0cd8e2e 100644 --- a/src/main/java/org/jabref/logic/openoffice/frontend/UpdateCitationMarkers.java +++ b/src/main/java/org/jabref/logic/openoffice/frontend/UpdateCitationMarkers.java @@ -22,34 +22,26 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/* - * Update document: citation marks and bibliography - */ public class UpdateCitationMarkers { private static final Logger LOGGER = LoggerFactory.getLogger(UpdateCitationMarkers.class); private UpdateCitationMarkers() { - /**/ } /** - * Visit each reference mark in referenceMarkNames, overwrite its - * text content. - * - * After each fillCitationMarkInCursor call check if we lost the - * BIB_SECTION_NAME bookmark and recreate it if we did. + * Visit each reference mark in referenceMarkNames, overwrite its text content. + *

+ * After each fillCitationMarkInCursor call check if we lost the BIB_SECTION_NAME bookmark and recreate it if we did. * * @param frontend - * - * @param style Bibliography style to use. - * + * @param style Bibliography style to use. */ public static void applyNewCitationMarkers(XTextDocument doc, OOFrontend frontend, OOBibStyle style) - throws - NoDocumentException, - CreationException, - WrappedTargetException { + throws + NoDocumentException, + CreationException, + WrappedTargetException { CitationGroups citationGroups = frontend.citationGroups; @@ -58,9 +50,9 @@ public static void applyNewCitationMarkers(XTextDocument doc, OOFrontend fronten boolean withText = (group.citationType != CitationType.INVISIBLE_CIT); Optional marker = group.getCitationMarker(); - if (!marker.isPresent()) { + if (marker.isEmpty()) { LOGGER.warn("applyNewCitationMarkers: no marker for {}", - group.groupId.citationGroupIdAsString()); + group.groupId.citationGroupIdAsString()); continue; } @@ -72,7 +64,6 @@ public static void applyNewCitationMarkers(XTextDocument doc, OOFrontend fronten frontend.cleanFillCursorForCitationGroup(doc, group); } - } } @@ -81,10 +72,10 @@ public static void fillCitationMarkInCursor(XTextDocument doc, OOText citationText, boolean withText, OOBibStyle style) - throws - WrappedTargetException, - CreationException, - IllegalArgumentException { + throws + WrappedTargetException, + CreationException, + IllegalArgumentException { Objects.requireNonNull(cursor); Objects.requireNonNull(citationText); @@ -102,21 +93,15 @@ public static void fillCitationMarkInCursor(XTextDocument doc, } /** - * Inserts a citation group in the document: creates and fills it. + * Inserts a citation group in the document: creates and fills it. * - * @param citationKeys BibTeX keys of + * @param citationKeys BibTeX keys of * @param pageInfos * @param citationType - * - * @param citationText Text for the citation. A citation mark or - * placeholder if not yet available. - * - * @param position Location to insert at. + * @param citationText Text for the citation. A citation mark or placeholder if not yet available. + * @param position Location to insert at. * @param style - * @param insertSpaceAfter A space inserted after the reference - * mark makes it easier to separate from the text - * coming after. But is not wanted when we recreate a - * reference mark. + * @param insertSpaceAfter A space inserted after the reference mark makes it easier to separate from the text coming after. But is not wanted when we recreate a reference mark. */ public static void createAndFillCitationGroup(OOFrontend frontend, XTextDocument doc, @@ -127,25 +112,25 @@ public static void createAndFillCitationGroup(OOFrontend frontend, XTextCursor position, OOBibStyle style, boolean insertSpaceAfter) - throws - NotRemoveableException, - WrappedTargetException, - PropertyVetoException, - IllegalArgumentException, - CreationException, - NoDocumentException, - IllegalTypeException { + throws + NotRemoveableException, + WrappedTargetException, + PropertyVetoException, + IllegalArgumentException, + CreationException, + NoDocumentException, + IllegalTypeException { Objects.requireNonNull(pageInfos); if (pageInfos.size() != citationKeys.size()) { throw new IllegalArgumentException("pageInfos.size != citationKeys.size"); } CitationGroup group = frontend.createCitationGroup(doc, - citationKeys, - pageInfos, - citationType, - position, - insertSpaceAfter); + citationKeys, + pageInfos, + citationType, + position, + insertSpaceAfter); final boolean withText = citationType.withText(); @@ -158,5 +143,4 @@ public static void createAndFillCitationGroup(OOFrontend frontend, } position.collapseToEnd(); } - } diff --git a/src/main/java/org/jabref/logic/openoffice/style/OOBibStyle.java b/src/main/java/org/jabref/logic/openoffice/style/OOBibStyle.java index c9ee3043509..fcac7dff56c 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOBibStyle.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOBibStyle.java @@ -1,13 +1,12 @@ package org.jabref.logic.openoffice.style; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; -import java.util.ArrayList; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -23,11 +22,6 @@ import org.jabref.logic.layout.LayoutFormatter; import org.jabref.logic.layout.LayoutFormatterPreferences; import org.jabref.logic.layout.LayoutHelper; -import org.jabref.model.database.BibDatabase; -import org.jabref.model.entry.Author; -import org.jabref.model.entry.AuthorList; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldFactory; import org.jabref.model.entry.field.OrFields; import org.jabref.model.entry.field.StandardField; @@ -40,7 +34,6 @@ import org.jabref.model.openoffice.style.CitationMarkerNumericBibEntry; import org.jabref.model.openoffice.style.CitationMarkerNumericEntry; import org.jabref.model.openoffice.style.NonUniqueCitationMarker; -import org.jabref.model.strings.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -140,18 +133,18 @@ public class OOBibStyle implements Comparable { private String name = ""; private Layout defaultBibLayout; private boolean valid; - private File styleFile; + private Path styleFile; private long styleFileModificationTime = Long.MIN_VALUE; private String localCopy; private boolean isDefaultLayoutPresent; - public OOBibStyle(File styleFile, LayoutFormatterPreferences prefs) throws IOException { + public OOBibStyle(Path styleFile, LayoutFormatterPreferences prefs) throws IOException { this.prefs = Objects.requireNonNull(prefs); this.styleFile = Objects.requireNonNull(styleFile); setDefaultProperties(); reload(); fromResource = false; - path = styleFile.getPath(); + path = styleFile.toAbsolutePath().toString(); } public OOBibStyle(String resourcePath, LayoutFormatterPreferences prefs) throws IOException { @@ -228,7 +221,7 @@ public String getPath() { return path; } - public File getFile() { + public Path getFile() { return styleFile; } @@ -264,8 +257,8 @@ public void ensureUpToDate() throws IOException { */ private void reload() throws IOException { if (styleFile != null) { - this.styleFileModificationTime = styleFile.lastModified(); - try (InputStream stream = new FileInputStream(styleFile)) { + this.styleFileModificationTime = Files.getLastModifiedTime(styleFile).toMillis(); + try (InputStream stream = Files.newInputStream(styleFile)) { initialize(stream); } } @@ -281,7 +274,11 @@ private boolean isUpToDate() { if (styleFile == null) { return true; } else { - return styleFile.lastModified() == this.styleFileModificationTime; + try { + return Files.getLastModifiedTime(styleFile).toMillis() == this.styleFileModificationTime; + } catch (IOException e) { + return false; + } } } @@ -440,351 +437,6 @@ public Layout getReferenceFormat(EntryType type) { } } - /* begin_old */ - /** - * Format a number-based citation marker for the given number. - * - * @param number The citation numbers. - * @return The text for the citation. - */ - public String getNumCitationMarker(List number, int minGroupingCount, boolean inList) { - String bracketBefore = getStringCitProperty(BRACKET_BEFORE); - if (inList && (citProperties.containsKey(BRACKET_BEFORE_IN_LIST))) { - bracketBefore = getStringCitProperty(BRACKET_BEFORE_IN_LIST); - } - String bracketAfter = getStringCitProperty(BRACKET_AFTER); - if (inList && (citProperties.containsKey(BRACKET_AFTER_IN_LIST))) { - bracketAfter = getStringCitProperty(BRACKET_AFTER_IN_LIST); - } - // Sort the numbers: - List lNum = new ArrayList<>(number); - Collections.sort(lNum); - StringBuilder stringBuilder = new StringBuilder(bracketBefore); - int combineFrom = -1; - int written = 0; - for (int i = 0; i < lNum.size(); i++) { - int i1 = lNum.get(i); - if (combineFrom < 0) { - // Check if next entry is the next in the ref list: - if ((i < (lNum.size() - 1)) && (lNum.get(i + 1) == (i1 + 1)) && (i1 > 0)) { - combineFrom = i1; - } else { - // Add single entry: - if (i > 0) { - stringBuilder.append(getStringCitProperty(CITATION_SEPARATOR)); - } - stringBuilder.append(lNum.get(i) > 0 ? String.valueOf(lNum.get(i)) : OOBibStyle.UNDEFINED_CITATION_MARKER); - written++; - } - } else { - // We are building a list of combined entries. - // Check if it ends here: - if ((i == (lNum.size() - 1)) || (lNum.get(i + 1) != (i1 + 1))) { - if (written > 0) { - stringBuilder.append(getStringCitProperty(CITATION_SEPARATOR)); - } - if ((minGroupingCount > 0) && (((i1 + 1) - combineFrom) >= minGroupingCount)) { - stringBuilder.append(combineFrom); - stringBuilder.append(getStringCitProperty(GROUPED_NUMBERS_SEPARATOR)); - stringBuilder.append(i1); - written++; - } else { - // Either we should never group, or there aren't enough - // entries in this case to group. Output all: - for (int jj = combineFrom; jj <= i1; jj++) { - stringBuilder.append(jj); - if (jj < i1) { - stringBuilder.append(getStringCitProperty(CITATION_SEPARATOR)); - } - written++; - } - } - combineFrom = -1; - } - // If it doesn't end here, just keep iterating. - } - } - stringBuilder.append(bracketAfter); - return stringBuilder.toString(); - } - /* end_old */ - - /* begin_old */ - public String getCitationMarker(List entries, Map database, boolean inParenthesis, - String[] uniquefiers, int[] unlimAuthors) { - // Look for groups of uniquefied entries that should be combined in the output. - // E.g. (Olsen, 2005a, b) should be output instead of (Olsen, 2005a; Olsen, 2005b). - int piv = -1; - String tmpMarker = null; - if (uniquefiers != null) { - for (int i = 0; i < uniquefiers.length; i++) { - - if ((uniquefiers[i] == null) || uniquefiers[i].isEmpty()) { - // This entry has no uniquefier. - // Check if we just passed a group of more than one entry with uniquefier: - if ((piv > -1) && (i > (piv + 1))) { - // Do the grouping: - group(entries, uniquefiers, piv, i - 1); - } - - piv = -1; - } else { - BibEntry currentEntry = entries.get(i); - if (piv == -1) { - piv = i; - tmpMarker = getAuthorYearParenthesisMarker(Collections.singletonList(currentEntry), database, - null, unlimAuthors); - } else { - // See if this entry can go into a group with the previous one: - String thisMarker = getAuthorYearParenthesisMarker(Collections.singletonList(currentEntry), - database, null, unlimAuthors); - - String authorField = getStringCitProperty(AUTHOR_FIELD); - int maxAuthors = getIntCitProperty(MAX_AUTHORS); - String author = getCitationMarkerField(currentEntry, database.get(currentEntry), - authorField); - AuthorList al = AuthorList.parse(author); - int prevALim = unlimAuthors[i - 1]; // i always at least 1 here - if (!thisMarker.equals(tmpMarker) - || ((al.getNumberOfAuthors() > maxAuthors) && (unlimAuthors[i] != prevALim))) { - // No match. Update piv to exclude the previous entry. But first check if the - // previous entry was part of a group: - if ((piv > -1) && (i > (piv + 1))) { - // Do the grouping: - group(entries, uniquefiers, piv, i - 1); - } - tmpMarker = thisMarker; - piv = i; - } - } - } - - } - // Finished with the loop. See if the last entries form a group: - if (piv >= 0) { - // Do the grouping: - group(entries, uniquefiers, piv, uniquefiers.length - 1); - } - } - - if (inParenthesis) { - return getAuthorYearParenthesisMarker(entries, database, uniquefiers, unlimAuthors); - } else { - return getAuthorYearInTextMarker(entries, database, uniquefiers, unlimAuthors); - } - } - /* end_old */ - - /* begin_old */ - /** - * Modify entry and uniquefier arrays to facilitate a grouped presentation of uniquefied entries. - * - * @param entries The entry array. - * @param uniquefiers The uniquefier array. - * @param from The first index to group (inclusive) - * @param to The last index to group (inclusive) - */ - private void group(List entries, String[] uniquefiers, int from, int to) { - String separator = getStringCitProperty(UNIQUEFIER_SEPARATOR); - StringBuilder stringBuilder = new StringBuilder(uniquefiers[from]); - for (int i = from + 1; i <= to; i++) { - stringBuilder.append(separator); - stringBuilder.append(uniquefiers[i]); - entries.set(i, null); - } - uniquefiers[from] = stringBuilder.toString(); - } - /* end_old */ - - /* begin_old */ - /** - * This method produces (Author, year) style citation strings in many different forms. - * - * @param entries The list of BibEntry to get fields from. - * @param database A map of BibEntry-BibDatabase pairs. - * @param uniquifiers Optional parameter to separate similar citations. Elements can be null if not needed. - * @return The formatted citation. - */ - private String getAuthorYearParenthesisMarker(List entries, Map database, - String[] uniquifiers, int[] unlimAuthors) { - - String authorField = getStringCitProperty(AUTHOR_FIELD); // The bibtex field providing author names, e.g. "author" or "editor". - int maxA = getIntCitProperty(MAX_AUTHORS); // The maximum number of authors to write out in full without using etal. Set to - // -1 to always write out all authors. - String yearSep = getStringCitProperty(YEAR_SEPARATOR); // The String to separate authors from year, e.g. "; ". - String startBrace = getStringCitProperty(BRACKET_BEFORE); // The opening parenthesis. - String endBrace = getStringCitProperty(BRACKET_AFTER); // The closing parenthesis. - String citationSeparator = getStringCitProperty(CITATION_SEPARATOR); // The String to separate citations from each other. - String yearField = getStringCitProperty(YEAR_FIELD); // The bibtex field providing the year, e.g. "year". - String andString = getStringCitProperty(AUTHOR_LAST_SEPARATOR); // The String to add between the two last author names, e.g. " & ". - StringBuilder stringBuilder = new StringBuilder(startBrace); - for (int j = 0; j < entries.size(); j++) { - BibEntry currentEntry = entries.get(j); - - // Check if this entry has been nulled due to grouping with the previous entry(ies): - if (currentEntry == null) { - continue; - } - - if (j > 0) { - stringBuilder.append(citationSeparator); - } - - BibDatabase currentDatabase = database.get(currentEntry); - int unlimA = unlimAuthors == null ? -1 : unlimAuthors[j]; - int maxAuthors = unlimA > 0 ? unlimA : maxA; - - String author = getCitationMarkerField(currentEntry, currentDatabase, authorField); - String authorString = createAuthorList(author, maxAuthors, andString, yearSep); - stringBuilder.append(authorString); - String year = getCitationMarkerField(currentEntry, currentDatabase, yearField); - if (year != null) { - stringBuilder.append(year); - } - if ((uniquifiers != null) && (uniquifiers[j] != null)) { - stringBuilder.append(uniquifiers[j]); - } - } - stringBuilder.append(endBrace); - return stringBuilder.toString(); - } - /* end_old */ - - /* begin_old */ - /** - * This method produces "Author (year)" style citation strings in many different forms. - * - * @param entries The list of BibEntry to get fields from. - * @param database A map of BibEntry-BibDatabase pairs. - * @param uniquefiers Optional parameters to separate similar citations. Can be null if not needed. - * @return The formatted citation. - */ - private String getAuthorYearInTextMarker(List entries, Map database, - String[] uniquefiers, - int[] unlimAuthors) { - String authorField = getStringCitProperty(AUTHOR_FIELD); // The bibtex field providing author names, e.g. "author" or "editor". - int maxA = getIntCitProperty(MAX_AUTHORS); // The maximum number of authors to write out in full without using etal. Set to - // -1 to always write out all authors. - String yearSep = getStringCitProperty(IN_TEXT_YEAR_SEPARATOR); // The String to separate authors from year, e.g. "; ". - String startBrace = getStringCitProperty(BRACKET_BEFORE); // The opening parenthesis. - String endBrace = getStringCitProperty(BRACKET_AFTER); // The closing parenthesis. - String citationSeparator = getStringCitProperty(CITATION_SEPARATOR); // The String to separate citations from each other. - String yearField = getStringCitProperty(YEAR_FIELD); // The bibtex field providing the year, e.g. "year". - String andString = getStringCitProperty(AUTHOR_LAST_SEPARATOR_IN_TEXT); // The String to add between the two last author names, e.g. " & ". - - if (andString == null) { - // Use the default one if no explicit separator for text is defined - andString = getStringCitProperty(AUTHOR_LAST_SEPARATOR); - } - StringBuilder stringBuilder = new StringBuilder(); - for (int i = 0; i < entries.size(); i++) { - BibEntry currentEntry = entries.get(i); - - // Check if this entry has been nulled due to grouping with the previous entry(ies): - if (currentEntry == null) { - continue; - } - - BibDatabase currentDatabase = database.get(currentEntry); - int unlimA = unlimAuthors == null ? -1 : unlimAuthors[i]; - int maxAuthors = unlimA > 0 ? unlimA : maxA; - - if (i > 0) { - stringBuilder.append(citationSeparator); - } - String author = getCitationMarkerField(currentEntry, currentDatabase, authorField); - String authorString = createAuthorList(author, maxAuthors, andString, yearSep); - stringBuilder.append(authorString); - stringBuilder.append(startBrace); - String year = getCitationMarkerField(currentEntry, currentDatabase, yearField); - if (year != null) { - stringBuilder.append(year); - } - if ((uniquefiers != null) && (uniquefiers[i] != null)) { - stringBuilder.append(uniquefiers[i]); - } - stringBuilder.append(endBrace); - } - return stringBuilder.toString(); - - } - /* end_old */ - - /* begin_old */ - /* moved to OOBibStyleGetCitationMarker */ - /** - * This method looks up a field for an entry in a database. Any number of backup fields can be used - * if the primary field is empty. - * - * @param entry The entry. - * @param database The database the entry belongs to. - * @param fields The field, or succession of fields, to look up. If backup fields are needed, separate - * field names by /. E.g. to use "author" with "editor" as backup, specify StandardField.orFields(StandardField.AUTHOR, StandardField.EDITOR). - * @return The resolved field content, or an empty string if the field(s) were empty. - */ - private String getCitationMarkerField(BibEntry entry, BibDatabase database, String fields) { - Objects.requireNonNull(entry, "Entry cannot be null"); - Objects.requireNonNull(database, "database cannot be null"); - - Set authorFields = FieldFactory.parseOrFields(getStringCitProperty(AUTHOR_FIELD)); - for (Field field : FieldFactory.parseOrFields(fields)) { - Optional content = entry.getResolvedFieldOrAlias(field, database); - - if ((content.isPresent()) && !content.get().trim().isEmpty()) { - if (authorFields.contains(field) && StringUtil.isInCurlyBrackets(content.get())) { - return "{" + fieldFormatter.format(content.get()) + "}"; - } - return fieldFormatter.format(content.get()); - } - } - // No luck? Return an empty string: - return ""; - } - /* end_old */ - - /* begin_old */ - /* moved to OOBibStyleGetCitationMarker */ - /** - * Look up the nth author and return the proper last name for citation markers. - * - * @param al The author list. - * @param number The number of the author to return. - * @return The author name, or an empty String if inapplicable. - */ - private String getAuthorLastName(AuthorList al, int number) { - StringBuilder stringBuilder = new StringBuilder(); - - if (al.getNumberOfAuthors() > number) { - Author a = al.getAuthor(number); - a.getVon().filter(von -> !von.isEmpty()).ifPresent(von -> stringBuilder.append(von).append(' ')); - stringBuilder.append(a.getLast().orElse("")); - } - - return stringBuilder.toString(); - } - /* end_old */ - - /* begin_old */ - /* to be removed */ - /** - * Take a finished citation and insert a string at the end (but inside the end bracket) - * separated by "PageInfoSeparator" - * - * @param citation - * @param pageInfo - * @return - */ - public String insertPageInfo(String citation, String pageInfo) { - String bracketAfter = getStringCitProperty(BRACKET_AFTER); - if (citation.endsWith(bracketAfter)) { - String first = citation.substring(0, citation.length() - bracketAfter.length()); - return first + getStringCitProperty(PAGE_INFO_SEPARATOR) + pageInfo + bracketAfter; - } else { - return citation + getStringCitProperty(PAGE_INFO_SEPARATOR) + pageInfo; - } - } - /* end_old */ - /** * Convenience method for checking the property for whether we use number citations or * author-year citations. @@ -910,39 +562,6 @@ public int hashCode() { return Objects.hash(path, name, citProperties, properties); } - /* begin_old */ - /* moved to OOBibStyleGetCitationMarker as formatAuthorList */ - private String createAuthorList(String author, int maxAuthors, String andString, - String yearSep) { - Objects.requireNonNull(author); - String etAlString = getStringCitProperty(ET_AL_STRING); // The String to represent authors that are not mentioned, e.g. " et al." - String authorSep = getStringCitProperty(AUTHOR_SEPARATOR); // The String to add between author names except the last two, e.g. ", ". - String oxfordComma = getStringCitProperty(OXFORD_COMMA); // The String to put after the second to last author in case of three or more authors - StringBuilder stringBuilder = new StringBuilder(); - AuthorList al = AuthorList.parse(author); - if (!al.isEmpty()) { - stringBuilder.append(getAuthorLastName(al, 0)); - } - if ((al.getNumberOfAuthors() > 1) && ((al.getNumberOfAuthors() <= maxAuthors) || (maxAuthors < 0))) { - int j = 1; - while (j < (al.getNumberOfAuthors() - 1)) { - stringBuilder.append(authorSep); - stringBuilder.append(getAuthorLastName(al, j)); - j++; - } - if (al.getNumberOfAuthors() > 2) { - stringBuilder.append(oxfordComma); - } - stringBuilder.append(andString); - stringBuilder.append(getAuthorLastName(al, al.getNumberOfAuthors() - 1)); - } else if (al.getNumberOfAuthors() > maxAuthors) { - stringBuilder.append(etAlString); - } - stringBuilder.append(yearSep); - return stringBuilder.toString(); - } - /* end_old */ - enum BibStyleMode { NONE, LAYOUT, diff --git a/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java b/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java index 2e5227d080e..e44b3ac9d3f 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java @@ -23,7 +23,6 @@ class OOBibStyleGetCitationMarker { private OOBibStyleGetCitationMarker() { - /**/ } /** diff --git a/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetNumCitationMarker.java b/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetNumCitationMarker.java index 60b5222cf5b..28ef8ac12cf 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetNumCitationMarker.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetNumCitationMarker.java @@ -12,13 +12,10 @@ class OOBibStyleGetNumCitationMarker { - /* - * The number encoding "this entry is unresolved" - */ + // The number encoding "this entry is unresolved" public final static int UNRESOLVED_ENTRY_NUMBER = 0; private OOBibStyleGetNumCitationMarker() { - /**/ } /** @@ -159,7 +156,6 @@ private static void emitBlock(List block, stringBuilder.append(block.get(j).getNumber().get()); } } - return; } } diff --git a/src/main/java/org/jabref/logic/openoffice/style/OOFormatBibliography.java b/src/main/java/org/jabref/logic/openoffice/style/OOFormatBibliography.java index ea311193068..38034693d16 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOFormatBibliography.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOFormatBibliography.java @@ -29,20 +29,20 @@ private OOFormatBibliography() { /** * @return The formatted bibliography, including its title. */ - public static OOText formatBibliography(CitationGroups cgs, + public static OOText formatBibliography(CitationGroups citationGroups, CitedKeys bibliography, OOBibStyle style, boolean alwaysAddCitedOnPages) { OOText title = style.getFormattedBibliographyTitle(); - OOText body = formatBibliographyBody(cgs, bibliography, style, alwaysAddCitedOnPages); + OOText body = formatBibliographyBody(citationGroups, bibliography, style, alwaysAddCitedOnPages); return OOText.fromString(title.toString() + body.toString()); } /** * @return Formatted body of the bibliography. Excludes the title. */ - public static OOText formatBibliographyBody(CitationGroups cgs, + public static OOText formatBibliographyBody(CitationGroups citationGroups, CitedKeys bibliography, OOBibStyle style, boolean alwaysAddCitedOnPages) { @@ -50,7 +50,7 @@ public static OOText formatBibliographyBody(CitationGroups cgs, StringBuilder stringBuilder = new StringBuilder(); for (CitedKey citedKey : bibliography.values()) { - OOText entryText = formatBibliographyEntry(cgs, citedKey, style, alwaysAddCitedOnPages); + OOText entryText = formatBibliographyEntry(citationGroups, citedKey, style, alwaysAddCitedOnPages); stringBuilder.append(entryText.toString()); } @@ -60,7 +60,7 @@ public static OOText formatBibliographyBody(CitationGroups cgs, /** * @return A paragraph. Includes label and "Cited on pages". */ - public static OOText formatBibliographyEntry(CitationGroups cgs, + public static OOText formatBibliographyEntry(CitationGroups citationGroups, CitedKey citedKey, OOBibStyle style, boolean alwaysAddCitedOnPages) { @@ -79,7 +79,7 @@ public static OOText formatBibliographyEntry(CitationGroups cgs, // Add "Cited on pages" if (citedKey.getLookupResult().isEmpty() || alwaysAddCitedOnPages) { - stringBuilder.append(formatCitedOnPages(cgs, citedKey).toString()); + stringBuilder.append(formatCitedOnPages(citationGroups, citedKey).toString()); } // Add paragraph @@ -154,9 +154,9 @@ private static OOText formatFullReferenceOfBibEntry(Layout layout, * - The links are created as references that show page numbers of the reference marks. * - We do not control the text shown, that is provided by OpenOffice. */ - private static OOText formatCitedOnPages(CitationGroups cgs, CitedKey citedKey) { + private static OOText formatCitedOnPages(CitationGroups citationGroups, CitedKey citedKey) { - if (!cgs.citationGroupsProvideReferenceMarkNameForLinking()) { + if (!citationGroups.citationGroupsProvideReferenceMarkNameForLinking()) { return OOText.fromString(""); } @@ -166,25 +166,25 @@ private static OOText formatCitedOnPages(CitationGroups cgs, CitedKey citedKey) final String suffix = ")"; stringBuilder.append(prefix); - List citationGroups = new ArrayList<>(); - for (CitationPath p : citedKey.getCitationPaths()) { - CitationGroupId groupId = p.group; - Optional group = cgs.getCitationGroup(groupId); + List filteredList = new ArrayList<>(); + for (CitationPath path : citedKey.getCitationPaths()) { + CitationGroupId groupId = path.group; + Optional group = citationGroups.getCitationGroup(groupId); if (group.isEmpty()) { throw new IllegalStateException(); } - citationGroups.add(group.get()); + filteredList.add(group.get()); } // sort the citationGroups according to their indexInGlobalOrder - citationGroups.sort((a, b) -> { + filteredList.sort((a, b) -> { Integer aa = a.getIndexInGlobalOrder().orElseThrow(IllegalStateException::new); Integer bb = b.getIndexInGlobalOrder().orElseThrow(IllegalStateException::new); return (aa.compareTo(bb)); }); int index = 0; - for (CitationGroup group : citationGroups) { + for (CitationGroup group : filteredList) { if (index > 0) { stringBuilder.append(", "); } diff --git a/src/main/java/org/jabref/logic/openoffice/style/OOPreFormatter.java b/src/main/java/org/jabref/logic/openoffice/style/OOPreFormatter.java index dc02daf6ee9..caeaf8cce2a 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOPreFormatter.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOPreFormatter.java @@ -8,8 +8,7 @@ import org.jabref.model.strings.StringUtil; /** - * This formatter preprocesses JabRef fields before they are run through the layout of the - * bibliography style. It handles translation of LaTeX italic/bold commands into HTML tags. + * This formatter preprocesses JabRef fields before they are run through the layout of the bibliography style. It handles translation of LaTeX italic/bold commands into HTML tags. */ public class OOPreFormatter implements LayoutFormatter { @@ -46,7 +45,6 @@ public String format(String field) { currentCommand = new StringBuilder(); } else if (!incommand && ((c == '{') || (c == '}'))) { // Swallow braces, necessary for replacing encoded characters - } else if (Character.isLetter(c) || (c == '%') || StringUtil.SPECIAL_COMMAND_CHARS.contains(String.valueOf(c))) { escaped = false; @@ -88,10 +86,7 @@ public String format(String field) { if ((i + 1) == finalResult.length()) { String command = currentCommand.toString(); String result = OOPreFormatter.CHARS.get(command); - /* If found, then use translated version. If not, - * then keep - * the text of the parameter intact. - */ + // If found, then use translated version. If not, then keep the text of the parameter intact. sb.append(Objects.requireNonNullElse(result, command)); } } @@ -104,9 +99,7 @@ public String format(String field) { } else if (Character.isWhitespace(c) || (c == '{') || (c == '}')) { String command = currentCommand.toString(); - // Test if we are dealing with a formatting - // command. - // If so, handle. + // Test if we are dealing with a formatting command. If so, handle. String tag = getHTMLTag(command); if (!tag.isEmpty()) { String part = StringUtil.getPart(finalResult, i, true); @@ -118,14 +111,11 @@ public String format(String field) { argument = part; // handle common case of general latex command String result = OOPreFormatter.CHARS.get(command + argument); - // If found, then use translated version. If not, then keep - // the - // text of the parameter intact. + // If found, then use translated version. If not, then keep the text of the parameter intact. sb.append(Objects.requireNonNullElse(result, argument)); } else if (c == '}') { - // This end brace terminates a command. This can be the case in - // constructs like {\aa}. The correct behaviour should be to - // substitute the evaluated command and swallow the brace: + // This end brace terminates a command. This can be the case in constructs like {\aa}. The + // correct behaviour should be to substitute the evaluated command and swallow the brace: String result = OOPreFormatter.CHARS.get(command); // If the command is unknown, just print it: sb.append(Objects.requireNonNullElse(result, command)); @@ -139,13 +129,10 @@ public String format(String field) { // argument = ""; } else { /* - * TODO: this point is reached, apparently, if a command is - * terminated in a strange way, such as with "$\omega$". - * Also, the command "\&" causes us to get here. The former - * issue is maybe a little difficult to address, since it - * involves the LaTeX math mode. We don't have a complete - * LaTeX parser, so maybe it's better to ignore these - * commands? + * TODO: this point is reached, apparently, if a command is terminated in a strange way, such as + * with "$\omega$". Also, the command "\&" causes us to get here. The former issue is maybe a + * little difficult to address, since it involves the LaTeX math mode. We don't have a complete + * LaTeX parser, so maybe it's better to ignore these commands? */ } @@ -160,44 +147,17 @@ public String format(String field) { private String getHTMLTag(String latexCommand) { String result = ""; switch (latexCommand) { - // Italic - case "textit": - case "it": - case "emph": // Should really separate between emphasized and italic but since in later stages both are converted to italic... - case "em": - result = "i"; - break; - // Bold font - case "textbf": - case "bf": - result = "b"; - break; - // Small capitals - case "textsc": - result = "smallcaps"; // Not a proper HTML tag, but used here for convenience - break; - // Underline - case "underline": - result = "u"; - break; - // Strikeout, sout is the "standard" command, although it is actually based on the package ulem - case "sout": - result = "s"; - break; - // Monospace font - case "texttt": - result = "tt"; - break; - // Superscript - case "textsuperscript": - result = "sup"; - break; - // Subscript - case "textsubscript": - result = "sub"; - break; - default: - break; + // Should really separate between emphasized and italic but since in later stages both are converted to italic... + case "textit", "it", "emph", "em" -> result = "i"; // Italic + case "textbf", "bf" -> result = "b"; // Bold font + case "textsc" -> result = "smallcaps"; // Small caps + // Not a proper HTML tag, but used here for convenience + case "underline" -> result = "u"; // Underline + case "sout" -> result = "s"; // Strikeout + // sout is the "standard" command, although it is actually based on the package ulem + case "texttt" -> result = "tt"; // Monospace font + case "textsuperscript" -> result = "sup"; // Superscript + case "textsubscript" -> result = "sub"; // Subscript } return result; } diff --git a/src/main/java/org/jabref/logic/openoffice/style/OOProcess.java b/src/main/java/org/jabref/logic/openoffice/style/OOProcess.java index f8f7de6c7b7..5fff6848133 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOProcess.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOProcess.java @@ -16,7 +16,6 @@ public class OOProcess { static final Comparator YEAR_AUTHOR_TITLE_COMPARATOR = makeYearAuthorTitleComparator(); private OOProcess() { - /**/ } private static Comparator makeAuthorYearTitleComparator() { @@ -51,25 +50,25 @@ public static Comparator comparatorForMulticite(OOBibStyle style) { } /** - * Fill cgs.bibliography and cgs.citationGroupsUnordered//CitationMarker + * Fill citationGroups.bibliography and cgs.citationGroupsUnordered//CitationMarker * according to style. */ - public static void produceCitationMarkers(CitationGroups cgs, List databases, OOBibStyle style) { + public static void produceCitationMarkers(CitationGroups citationGroups, List databases, OOBibStyle style) { - if (!cgs.hasGlobalOrder()) { - throw new IllegalStateException("produceCitationMarkers: globalOrder is misssing in cgs"); + if (!citationGroups.hasGlobalOrder()) { + throw new IllegalStateException("produceCitationMarkers: globalOrder is misssing in citationGroups"); } - cgs.lookupCitations(databases); - cgs.imposeLocalOrder(comparatorForMulticite(style)); + citationGroups.lookupCitations(databases); + citationGroups.imposeLocalOrder(comparatorForMulticite(style)); // fill CitationGroup.citationMarker if (style.isCitationKeyCiteMarkers()) { - OOProcessCitationKeyMarkers.produceCitationMarkers(cgs, style); + OOProcessCitationKeyMarkers.produceCitationMarkers(citationGroups, style); } else if (style.isNumberEntries()) { - OOProcessNumericMarkers.produceCitationMarkers(cgs, style); + OOProcessNumericMarkers.produceCitationMarkers(citationGroups, style); } else { - OOProcessAuthorYearMarkers.produceCitationMarkers(cgs, style); + OOProcessAuthorYearMarkers.produceCitationMarkers(citationGroups, style); } } diff --git a/src/main/java/org/jabref/logic/openoffice/style/OOProcessAuthorYearMarkers.java b/src/main/java/org/jabref/logic/openoffice/style/OOProcessAuthorYearMarkers.java index affbc7584ae..d01e3627104 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOProcessAuthorYearMarkers.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOProcessAuthorYearMarkers.java @@ -22,7 +22,6 @@ class OOProcessAuthorYearMarkers { private OOProcessAuthorYearMarkers() { - /**/ } /** @@ -46,11 +45,11 @@ private static void createNormalizedCitationMarkers(CitedKeys sortedCitedKeys, O * Clears uniqueLetters before filling. * * On return: Each citedKey in sortedCitedKeys has uniqueLetter set as needed. - * The same values are copied to the corresponding citations in cgs. + * The same values are copied to the corresponding citations in citationGroups. * * Depends on: style, citations and their order. */ - private static void createUniqueLetters(CitedKeys sortedCitedKeys, CitationGroups cgs) { + private static void createUniqueLetters(CitedKeys sortedCitedKeys, CitationGroups citationGroups) { // The entries in the clashingKeys lists preserve // firstAppearance order from sortedCitedKeys.values(). @@ -93,7 +92,7 @@ private static void createUniqueLetters(CitedKeys sortedCitedKeys, CitationGroup nextUniqueLetter++; } } - sortedCitedKeys.distributeUniqueLetters(cgs); + sortedCitedKeys.distributeUniqueLetters(citationGroups); } /* *************************************** @@ -108,9 +107,9 @@ private static void createUniqueLetters(CitedKeys sortedCitedKeys, CitationGroup * * Preconditions: globalOrder, localOrder */ - private static void setIsFirstAppearanceOfSourceInCitations(CitationGroups cgs) { + private static void setIsFirstAppearanceOfSourceInCitations(CitationGroups citationGroups) { Set seenBefore = new HashSet<>(); - for (CitationGroup group : cgs.getCitationGroupsInGlobalOrder()) { + for (CitationGroup group : citationGroups.getCitationGroupsInGlobalOrder()) { for (Citation cit : group.getCitationsInLocalOrder()) { String currentKey = cit.citationKey; if (!seenBefore.contains(currentKey)) { @@ -127,25 +126,25 @@ private static void setIsFirstAppearanceOfSourceInCitations(CitationGroups cgs) * Produce citMarkers for normal * (!isCitationKeyCiteMarkers && !isNumberEntries) styles. * - * @param cgs + * @param citationGroups * @param style Bibliography style. */ - static void produceCitationMarkers(CitationGroups cgs, OOBibStyle style) { + static void produceCitationMarkers(CitationGroups citationGroups, OOBibStyle style) { assert !style.isCitationKeyCiteMarkers(); assert !style.isNumberEntries(); // Citations in (Au1, Au2 2000) form - CitedKeys citedKeys = cgs.getCitedKeysSortedInOrderOfAppearance(); + CitedKeys citedKeys = citationGroups.getCitedKeysSortedInOrderOfAppearance(); createNormalizedCitationMarkers(citedKeys, style); - createUniqueLetters(citedKeys, cgs); - cgs.createPlainBibliographySortedByComparator(OOProcess.AUTHOR_YEAR_TITLE_COMPARATOR); + createUniqueLetters(citedKeys, citationGroups); + citationGroups.createPlainBibliographySortedByComparator(OOProcess.AUTHOR_YEAR_TITLE_COMPARATOR); // Mark first appearance of each citationKey - setIsFirstAppearanceOfSourceInCitations(cgs); + setIsFirstAppearanceOfSourceInCitations(citationGroups); - for (CitationGroup group : cgs.getCitationGroupsInGlobalOrder()) { + for (CitationGroup group : citationGroups.getCitationGroupsInGlobalOrder()) { final boolean inParenthesis = (group.citationType == CitationType.AUTHORYEAR_PAR); final NonUniqueCitationMarker strictlyUnique = NonUniqueCitationMarker.THROWS; diff --git a/src/main/java/org/jabref/logic/openoffice/style/OOProcessCitationKeyMarkers.java b/src/main/java/org/jabref/logic/openoffice/style/OOProcessCitationKeyMarkers.java index 5329d9ca021..16fe8279123 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOProcessCitationKeyMarkers.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOProcessCitationKeyMarkers.java @@ -11,20 +11,19 @@ class OOProcessCitationKeyMarkers { private OOProcessCitationKeyMarkers() { - /**/ } /** * Produce citation markers for the case when the citation * markers are the citation keys themselves, separated by commas. */ - static void produceCitationMarkers(CitationGroups cgs, OOBibStyle style) { + static void produceCitationMarkers(CitationGroups citationGroups, OOBibStyle style) { assert style.isCitationKeyCiteMarkers(); - cgs.createPlainBibliographySortedByComparator(OOProcess.AUTHOR_YEAR_TITLE_COMPARATOR); + citationGroups.createPlainBibliographySortedByComparator(OOProcess.AUTHOR_YEAR_TITLE_COMPARATOR); - for (CitationGroup group : cgs.getCitationGroupsInGlobalOrder()) { + for (CitationGroup group : citationGroups.getCitationGroupsInGlobalOrder()) { String citMarker = style.getCitationGroupMarkupBefore() + String.join(",", OOListUtil.map(group.getCitationsInLocalOrder(), Citation::getCitationKey)) diff --git a/src/main/java/org/jabref/logic/openoffice/style/OOProcessNumericMarkers.java b/src/main/java/org/jabref/logic/openoffice/style/OOProcessNumericMarkers.java index deddab66cca..3a6396817e6 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOProcessNumericMarkers.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOProcessNumericMarkers.java @@ -12,7 +12,6 @@ class OOProcessNumericMarkers { private OOProcessNumericMarkers() { - /**/ } /** @@ -23,21 +22,21 @@ private OOProcessNumericMarkers() { * Numbering is according to first appearance. * Assumes global order and local order are already applied. * - * @param cgs + * @param citationGroups * @param style * */ - static void produceCitationMarkers(CitationGroups cgs, OOBibStyle style) { + static void produceCitationMarkers(CitationGroups citationGroups, OOBibStyle style) { assert style.isNumberEntries(); if (style.isSortByPosition()) { - cgs.createNumberedBibliographySortedInOrderOfAppearance(); + citationGroups.createNumberedBibliographySortedInOrderOfAppearance(); } else { - cgs.createNumberedBibliographySortedByComparator(OOProcess.AUTHOR_YEAR_TITLE_COMPARATOR); + citationGroups.createNumberedBibliographySortedByComparator(OOProcess.AUTHOR_YEAR_TITLE_COMPARATOR); } - for (CitationGroup group : cgs.getCitationGroupsInGlobalOrder()) { + for (CitationGroup group : citationGroups.getCitationGroupsInGlobalOrder()) { List cits = OOListUtil.map(group.getCitationsInLocalOrder(), e -> e); OOText citMarker = style.getNumCitationMarker2(cits); group.setCitationMarker(Optional.of(citMarker)); diff --git a/src/main/java/org/jabref/logic/openoffice/style/StyleLoader.java b/src/main/java/org/jabref/logic/openoffice/style/StyleLoader.java index fa2a9f6ec76..90e1bc50158 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/StyleLoader.java +++ b/src/main/java/org/jabref/logic/openoffice/style/StyleLoader.java @@ -1,8 +1,8 @@ package org.jabref.logic.openoffice.style; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -55,7 +55,7 @@ public List getStyles() { public boolean addStyleIfValid(String filename) { Objects.requireNonNull(filename); try { - OOBibStyle newStyle = new OOBibStyle(new File(filename), layoutFormatterPreferences); + OOBibStyle newStyle = new OOBibStyle(Path.of(filename), layoutFormatterPreferences); if (externalStyles.contains(newStyle)) { LOGGER.info("External style file {} already existing.", filename); } else if (newStyle.isValid()) { @@ -80,7 +80,7 @@ private void loadExternalStyles() { List lists = openOfficePreferences.getExternalStyles(); for (String filename : lists) { try { - OOBibStyle style = new OOBibStyle(new File(filename), layoutFormatterPreferences); + OOBibStyle style = new OOBibStyle(Path.of(filename), layoutFormatterPreferences); if (style.isValid()) { // Problem! externalStyles.add(style); } else { diff --git a/src/main/java/org/jabref/model/openoffice/backend/NamedRange.java b/src/main/java/org/jabref/model/openoffice/backend/NamedRange.java index 63981ceba70..57fba4dc272 100644 --- a/src/main/java/org/jabref/model/openoffice/backend/NamedRange.java +++ b/src/main/java/org/jabref/model/openoffice/backend/NamedRange.java @@ -11,13 +11,9 @@ import com.sun.star.text.XTextRange; /** - * NamedRange (with NamedRangeManager) attempts to provide a common interface for working with - * reference mark based and bookmark based text ranges to be used as locations to fill with citation - * markers. LibreOffice supports name-based lookup and listing names for both (hence the name). - * - * Note: currently only implemented for refence marks (in NamedRangeReferenceMark and - * NamedRangeManagerReferenceMark). - * + * NamedRange (with NamedRangeManager) attempts to provide a common interface for working with reference mark based and bookmark based text ranges to be used as locations to fill with citation markers. LibreOffice supports name-based lookup and listing names for both (hence the name). + *

+ * Note: currently only implemented for refence marks (in NamedRangeReferenceMark and NamedRangeManagerReferenceMark). */ public interface NamedRange { @@ -27,45 +23,42 @@ public interface NamedRange { * @return Optional.empty if the mark is missing from the document. */ Optional getMarkRange(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException; + throws + NoDocumentException, + WrappedTargetException; /** - * Cursor for the reference marks as is, not prepared for filling, but does not need - * cleanFillCursor either. + * Cursor for the reference marks as is, not prepared for filling, but does not need cleanFillCursor either. */ Optional getRawCursor(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException; + throws + NoDocumentException, + WrappedTargetException; /** * Get a cursor for filling in text. - * + *

* Must be followed by cleanFillCursor() */ XTextCursor getFillCursor(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException, - CreationException; + throws + NoDocumentException, + WrappedTargetException, + CreationException; /** - * Remove brackets, but if the result would become empty, leave them; if the result would be a - * single character, leave the left bracket. - * + * Remove brackets, but if the result would become empty, leave them; if the result would be a single character, leave the left bracket. */ void cleanFillCursor(XTextDocument doc) - throws - NoDocumentException, - WrappedTargetException; + throws + NoDocumentException, + WrappedTargetException; /** - * Note: create is in NamedRangeManager + * Note: create is in NamedRangeManager */ void removeFromDocument(XTextDocument doc) - throws - WrappedTargetException, - NoDocumentException; + throws + WrappedTargetException, + NoDocumentException; } diff --git a/src/main/java/org/jabref/model/openoffice/backend/NamedRangeManager.java b/src/main/java/org/jabref/model/openoffice/backend/NamedRangeManager.java index 9bbcc752350..25a82b4ad19 100644 --- a/src/main/java/org/jabref/model/openoffice/backend/NamedRangeManager.java +++ b/src/main/java/org/jabref/model/openoffice/backend/NamedRangeManager.java @@ -17,15 +17,15 @@ NamedRange createNamedRange(XTextDocument doc, XTextCursor position, boolean insertSpaceAfter, boolean withoutBrackets) - throws - CreationException; + throws + CreationException; List getUsedNames(XTextDocument doc) - throws - NoDocumentException; + throws + NoDocumentException; Optional getNamedRangeFromDocument(XTextDocument doc, String markName) - throws - NoDocumentException, - WrappedTargetException; + throws + NoDocumentException, + WrappedTargetException; } diff --git a/src/main/java/org/jabref/model/openoffice/ootext/OOFormat.java b/src/main/java/org/jabref/model/openoffice/ootext/OOFormat.java index 6b4a96de644..14f5822de4f 100644 --- a/src/main/java/org/jabref/model/openoffice/ootext/OOFormat.java +++ b/src/main/java/org/jabref/model/openoffice/ootext/OOFormat.java @@ -4,40 +4,33 @@ /** * Helper functions to produce some of the markup as understood by OOTextIntoOO.write - * - * These do not cover all tags, only those needed to embed markup - * from Layout and citation marker formatters into citation markers and - * bibliography. + *

+ * These do not cover all tags, only those needed to embed markup from Layout and citation marker formatters into citation markers and bibliography. */ public class OOFormat { private OOFormat() { - /* */ } /** * Mark {@code ootext} as using a character locale known to OO. * * @param locale language[-country[-territory]] - * - * https://www.openoffice.org/api/docs/common/ref/com/sun/star/lang/Locale.html - * - * The country part is optional. - * - * The territory part is not only optional, the allowed "codes are vendor and browser-specific", - * so probably best to avoid them if possible. - * + *

+ * https://www.openoffice.org/api/docs/common/ref/com/sun/star/lang/Locale.html + *

+ * The country part is optional. + *

+ * The territory part is not only optional, the allowed "codes are vendor and browser-specific", so probably best to avoid them if possible. */ public static OOText setLocale(OOText ootext, String locale) { return OOText.fromString(String.format("", locale) + ootext.toString() + ""); } /** - * Mark {@code ootext} as using the character locale "zxx", which means "no language", "no - * linguistic content". - * + * Mark {@code ootext} as using the character locale "zxx", which means "no language", "no linguistic content". + *

* Used around citation marks, probably to turn off spellchecking. - * */ public static OOText setLocaleNone(OOText ootext) { return OOFormat.setLocale(ootext, "zxx"); @@ -46,14 +39,12 @@ public static OOText setLocaleNone(OOText ootext) { /** * Mark {@code ootext} using a character style {@code charStyle} * - * @param charStyle Name of a character style known to OO. May be empty for "Standard", which in - * turn means do not override any properties. - * + * @param charStyle Name of a character style known to OO. May be empty for "Standard", which in turn means do not override any properties. */ public static OOText setCharStyle(OOText ootext, String charStyle) { return OOText.fromString(String.format("", charStyle) - + ootext.toString() - + ""); + + ootext.toString() + + ""); } /** @@ -81,4 +72,4 @@ public static OOText formatReferenceToPageNumberOfReferenceMark(String reference String string = String.format("", referenceMarkName); return OOText.fromString(string); } - } +} diff --git a/src/main/java/org/jabref/model/openoffice/ootext/OOText.java b/src/main/java/org/jabref/model/openoffice/ootext/OOText.java index 5b5fa4caba9..8478ca4a65a 100644 --- a/src/main/java/org/jabref/model/openoffice/ootext/OOText.java +++ b/src/main/java/org/jabref/model/openoffice/ootext/OOText.java @@ -4,9 +4,8 @@ /** * Text with HTML-like markup as understood by OOTextIntoOO.write - * - * Some of the tags can be added using OOFormat methods. Others come from the layout engine, either - * by interpreting LaTeX markup or from settings in the jstyle file. + *

+ * Some of the tags can be added using OOFormat methods. Others come from the layout engine, either by interpreting LaTeX markup or from settings in the jstyle file. */ public class OOText { @@ -17,7 +16,9 @@ private OOText(String data) { this.data = data; } - /** @return null for null input, otherwise the argument wrapped into a new OOText */ + /** + * @return null for null input, otherwise the argument wrapped into a new OOText + */ public static OOText fromString(String string) { if (string == null) { return null; @@ -25,7 +26,9 @@ public static OOText fromString(String string) { return new OOText(string); } - /** @return null for null input, otherwise the string inside the argument */ + /** + * @return null for null input, otherwise the string inside the argument + */ public static String toString(OOText ootext) { if (ootext == null) { return null; diff --git a/src/main/java/org/jabref/model/openoffice/ootext/OOTextIntoOO.java b/src/main/java/org/jabref/model/openoffice/ootext/OOTextIntoOO.java index 909b088b5b3..bbe77c33e04 100644 --- a/src/main/java/org/jabref/model/openoffice/ootext/OOTextIntoOO.java +++ b/src/main/java/org/jabref/model/openoffice/ootext/OOTextIntoOO.java @@ -53,7 +53,7 @@ public class OOTextIntoOO { private static final Logger LOGGER = LoggerFactory.getLogger(OOTextIntoOO.class); /** - * "ParaStyleName" is an OpenOffice Property name. + * "ParaStyleName" is an OpenOffice Property name. */ private static final String PARA_STYLE_NAME = "ParaStyleName"; @@ -77,78 +77,60 @@ public class OOTextIntoOO { private static final byte SUBSCRIPT_HEIGHT = (byte) 58; private static final String TAG_NAME_REGEXP = - "(?:b|i|em|tt|smallcaps|sup|sub|u|s|p|span|oo:referenceToPageNumberOfReferenceMark)"; + "(?:b|i|em|tt|smallcaps|sup|sub|u|s|p|span|oo:referenceToPageNumberOfReferenceMark)"; private static final String ATTRIBUTE_NAME_REGEXP = - "(?:oo:ParaStyleName|oo:CharStyleName|lang|style|target)"; + "(?:oo:ParaStyleName|oo:CharStyleName|lang|style|target)"; private static final String ATTRIBUTE_VALUE_REGEXP = "\"([^\"]*)\""; private static final Pattern HTML_TAG = - Pattern.compile("<(/" + TAG_NAME_REGEXP + ")>" - + "|" - + "<(" + TAG_NAME_REGEXP + ")" - + "((?:\\s+(" + ATTRIBUTE_NAME_REGEXP + ")=" + ATTRIBUTE_VALUE_REGEXP + ")*)" - + ">"); + Pattern.compile("<(/" + TAG_NAME_REGEXP + ")>" + + "|" + + "<(" + TAG_NAME_REGEXP + ")" + + "((?:\\s+(" + ATTRIBUTE_NAME_REGEXP + ")=" + ATTRIBUTE_VALUE_REGEXP + ")*)" + + ">"); private static final Pattern ATTRIBUTE_PATTERN = - Pattern.compile("\\s+(" + ATTRIBUTE_NAME_REGEXP + ")=" + ATTRIBUTE_VALUE_REGEXP); + Pattern.compile("\\s+(" + ATTRIBUTE_NAME_REGEXP + ")=" + ATTRIBUTE_VALUE_REGEXP); private OOTextIntoOO() { - // Hide the public constructor } /** - * Insert a text with formatting indicated by HTML-like tags, into - * a text at the position given by a cursor. - * - * Limitation: understands no entities. It does not receive any either, unless - * the user provides it. - * - * To limit the damage {@code TAG_NAME_REGEXP} and {@code ATTRIBUTE_NAME_REGEXP} - * explicitly lists the names we care about. - * + * Insert a text with formatting indicated by HTML-like tags, into a text at the position given by a cursor. + *

+ * Limitation: understands no entities. It does not receive any either, unless the user provides it. + *

+ * To limit the damage {@code TAG_NAME_REGEXP} and {@code ATTRIBUTE_NAME_REGEXP} explicitly lists the names we care about. + *

* Notable changes w.r.t insertOOFormattedTextAtCurrentLocation: - * + *

* - new tags: + *

+ * - {@code } - earlier was applied from code + *

+ * - {@code } - earlier was applied from code, for "CitationCharacterFormat" + *

+ * - {@code

} start new paragraph - earlier was applied from code + *

+ * - {@code

} : start new paragraph and apply ParStyleName - earlier was applied from code + *

+ * - {@code } - earlier: known, but ignored - now: equivalent to {@code } - {@code } (self-closing) + *

+ * - closing tags try to properly restore state (in particular, the "not directly set" state) instead of dictating an "off" state. This makes a difference when the value inherited from another level (for example the paragraph) is not the "off" state. + *

+ * An example: a style with {@code ReferenceParagraphFormat="JR_bibentry"} Assume JR_bibentry in LibreOffice is a paragraph style that prescribes "bold" font. LAYOUT only prescribes bold around year. Which parts of the bibliography entries should come out as bold? + *

+ * - The user can format citation marks (it is enough to format their start) and the properties not (everywhere) dictated by the style are preserved (where they are not). * - * - {@code } - * - earlier was applied from code - * - * - {@code } - * - earlier was applied from code, for "CitationCharacterFormat" - * - * - {@code

} start new paragraph - * - earlier was applied from code - * - * - {@code

} : start new paragraph and apply ParStyleName - * - earlier was applied from code - * - * - {@code } - * - earlier: known, but ignored - * - now: equivalent to {@code } - * - {@code } (self-closing) - * - * - closing tags try to properly restore state (in particular, the "not directly set" state) - * instead of dictating an "off" state. This makes a difference when the value inherited from - * another level (for example the paragraph) is not the "off" state. - * - * An example: a style with - * {@code ReferenceParagraphFormat="JR_bibentry"} - * Assume JR_bibentry in LibreOffice is a paragraph style that prescribes "bold" font. - * LAYOUT only prescribes bold around year. - * Which parts of the bibliography entries should come out as bold? - * - * - The user can format citation marks (it is enough to format their start) and the - * properties not (everywhere) dictated by the style are preserved (where they are not). - * - * @param position The cursor giving the insert location. Not modified. - * @param ootext The marked-up text to insert. + * @param position The cursor giving the insert location. Not modified. + * @param ootext The marked-up text to insert. */ public static void write(XTextDocument doc, XTextCursor position, OOText ootext) - throws - WrappedTargetException, - CreationException { + throws + WrappedTargetException, + CreationException { Objects.requireNonNull(doc); Objects.requireNonNull(ootext); @@ -231,7 +213,7 @@ public static void write(XTextDocument doc, XTextCursor position, OOText ootext) case "oo:ParaStyleName": //

if (StringUtil.isNullOrEmpty(value)) { - LOGGER.debug(String.format("oo:ParaStyleName inherited")); + LOGGER.debug("oo:ParaStyleName inherited"); } else { if (setParagraphStyle(cursor, value)) { // Presumably tested already: @@ -250,12 +232,8 @@ public static void write(XTextDocument doc, XTextCursor position, OOText ootext) String key = pair.a; String value = pair.b; switch (key) { - case "target": - UnoCrossRef.insertReferenceToPageNumberOfReferenceMark(doc, value, cursor); - break; - default: - LOGGER.warn(String.format("Unexpected attribute '%s' for <%s>", key, tagName)); - break; + case "target" -> UnoCrossRef.insertReferenceToPageNumberOfReferenceMark(doc, value, cursor); + default -> LOGGER.warn(String.format("Unexpected attribute '%s' for <%s>", key, tagName)); } } break; @@ -270,27 +248,23 @@ public static void write(XTextDocument doc, XTextCursor position, OOText ootext) String key = pair.a; String value = pair.b; switch (key) { - case "oo:CharStyleName": - // - settings.addAll(setCharStyleName(value)); - break; - case "lang": - // - // - settings.addAll(setCharLocale(value)); - break; - case "style": + case "oo:CharStyleName" -> + // + settings.addAll(setCharStyleName(value)); + case "lang" -> + // + // + settings.addAll(setCharLocale(value)); + case "style" -> { // HTML-style small-caps if ("font-variant: small-caps".equals(value)) { settings.addAll(setCharCaseMap(CaseMap.SMALLCAPS)); break; } LOGGER.warn(String.format("Unexpected value %s for attribute '%s' for <%s>", - value, key, tagName)); - break; - default: - LOGGER.warn(String.format("Unexpected attribute '%s' for <%s>", key, tagName)); - break; + value, key, tagName)); + } + default -> LOGGER.warn(String.format("Unexpected attribute '%s' for <%s>", key, tagName)); } } formatStack.pushLayer(settings); @@ -310,9 +284,9 @@ public static void write(XTextDocument doc, XTextCursor position, OOText ootext) String expected = expectEnd.pop(); if (!tagName.equals(expected)) { LOGGER.warn(String.format("expected '<%s>', found '<%s>' after '%s'", - expected, - tagName, - currentSubstring)); + expected, + tagName, + currentSubstring)); } break; default: @@ -330,21 +304,18 @@ public static void write(XTextDocument doc, XTextCursor position, OOText ootext) cursor.collapseToEnd(); if (!expectEnd.empty()) { - String rest = ""; + StringBuilder rest = new StringBuilder(); for (String s : expectEnd) { - rest = String.format("<%s>", s) + rest; + rest.insert(0, String.format("<%s>", s)); } - LOGGER.warn(String.format("OOTextIntoOO.write:" - + " expectEnd stack is not empty at the end: %s%n", - rest)); + LOGGER.warn(String.format("OOTextIntoOO.write: expectEnd stack is not empty at the end: %s%n", rest)); } } /** - * Purpose: in some cases we do not want to inherit direct - * formatting from the context. - * - * In particular, when filling the bibliography title and body. + * Purpose: in some cases we do not want to inherit direct formatting from the context. + *

+ * In particular, when filling the bibliography title and body. */ public static void removeDirectFormatting(XTextCursor cursor) { @@ -358,8 +329,8 @@ public static void removeDirectFormatting(XTextCursor cursor) { propertySet.setPropertyValue(CHAR_STYLE_NAME, "Standard"); xPropertyState.setPropertyToDefault("CharCaseMap"); } catch (UnknownPropertyException | - PropertyVetoException | - WrappedTargetException ex) { + PropertyVetoException | + WrappedTargetException ex) { LOGGER.warn("exception caught", ex); } @@ -374,14 +345,14 @@ public static void removeDirectFormatting(XTextCursor cursor) { // Only report those we do not yet know about final Set knownToFail = Set.of("ListAutoFormat", - "ListId", - "NumberingIsNumber", - "NumberingLevel", - "NumberingRules", - "NumberingStartValue", - "ParaChapterNumberingLevel", - "ParaIsNumberingRestart", - "ParaStyleName"); + "ListId", + "NumberingIsNumber", + "NumberingLevel", + "NumberingRules", + "NumberingStartValue", + "ParaChapterNumberingLevel", + "ParaIsNumberingRestart", + "ParaStyleName"); // query again, just in case it matters propertySet = UnoCast.cast(XPropertySet.class, cursor).get(); @@ -422,34 +393,34 @@ static class MyPropertyStack { */ static final Set CONTROLLED_PROPERTIES = Set.of( - /* Used for SuperScript, SubScript. - * - * These three are interdependent: changing one may change others. - */ - "CharEscapement", "CharEscapementHeight", "CharAutoEscapement", + /* Used for SuperScript, SubScript. + * + * These three are interdependent: changing one may change others. + */ + "CharEscapement", "CharEscapementHeight", "CharAutoEscapement", - /* used for Bold */ - "CharWeight", + /* used for Bold */ + "CharWeight", - /* Used for Italic */ - "CharPosture", + /* Used for Italic */ + "CharPosture", - /* Used for strikeout. These two are interdependent. */ - "CharStrikeout", "CharCrossedOut", + /* Used for strikeout. These two are interdependent. */ + "CharStrikeout", "CharCrossedOut", - /* Used for underline. These three are interdependent, but apparently - * we can leave out the last two. - */ - "CharUnderline", // "CharUnderlineColor", "CharUnderlineHasColor", + /* Used for underline. These three are interdependent, but apparently + * we can leave out the last two. + */ + "CharUnderline", // "CharUnderlineColor", "CharUnderlineHasColor", - /* Used for lang="zxx", to silence spellchecker. */ - "CharLocale", + /* Used for lang="zxx", to silence spellchecker. */ + "CharLocale", - /* Used for CitationCharacterFormat. */ - "CharStyleName", + /* Used for CitationCharacterFormat. */ + "CharStyleName", - /* Used for and */ - "CharCaseMap"); + /* Used for and */ + "CharCaseMap"); /** * The number of properties actually controlled. @@ -467,9 +438,7 @@ static class MyPropertyStack { final String[] goodNames; /** - * Maintain a stack of layers, each containing a description of the desired state of - * properties. Each description is an ArrayList of property values, Optional.empty() - * encoding "not directly set". + * Maintain a stack of layers, each containing a description of the desired state of properties. Each description is an ArrayList of property values, Optional.empty() encoding "not directly set". */ final Stack>> layers; @@ -478,9 +447,7 @@ static class MyPropertyStack { XPropertySet propertySet = UnoCast.cast(XPropertySet.class, cursor).get(); XPropertySetInfo propertySetInfo = propertySet.getPropertySetInfo(); - /* - * On creation, initialize the property name -- index mapping. - */ + // On creation, initialize the property name -- index mapping. this.goodNameToIndex = new HashMap<>(); int nextIndex = 0; for (Property p : propertySetInfo.getProperties()) { @@ -498,19 +465,16 @@ static class MyPropertyStack { this.goodNames = new String[goodSize]; for (Map.Entry entry : goodNameToIndex.entrySet()) { - goodNames[ entry.getValue() ] = entry.getKey(); + goodNames[entry.getValue()] = entry.getKey(); } - // XMultiPropertySet.setPropertyValues() requires alphabetically sorted property names. - // We adjust here: + // XMultiPropertySet.setPropertyValues() requires alphabetically sorted property names. We adjust here: Arrays.sort(goodNames); for (int i = 0; i < goodSize; i++) { this.goodNameToIndex.put(goodNames[i], i); } - /* - * Get the initial state of the properties and add the first layer. - */ + // Get the initial state of the properties and add the first layer. XMultiPropertyStates mpss = UnoCast.cast(XMultiPropertyStates.class, cursor).get(); PropertyState[] propertyStates; try { @@ -537,9 +501,8 @@ static class MyPropertyStack { } /** - * Given a list of property name, property value pairs, construct and push a new layer - * describing the intended state after these have been applied. - * + * Given a list of property name, property value pairs, construct and push a new layer describing the intended state after these have been applied. + *

* Opening tags usually call this. */ void pushLayer(List> settings) { @@ -571,7 +534,7 @@ void popLayer() { /** * Apply the current desired formatting state to a cursor. - * + *

* The idea is to minimize the number of calls to OpenOffice. */ void apply(XTextCursor cursor) { @@ -629,24 +592,22 @@ private static List> parseAttributes(String attributes) { while (attributeMatcher.find()) { String key = attributeMatcher.group(1); String value = attributeMatcher.group(2); - res.add(new OOPair(key, value)); + res.add(new OOPair<>(key, value)); } return res; } - /* - * We rely on property values being either DIRECT_VALUE or DEFAULT_VALUE (not - * AMBIGUOUS_VALUE). If the cursor covers a homogeneous region, or is collapsed, then this is - * true. + /** + * We rely on property values being either DIRECT_VALUE or DEFAULT_VALUE (not AMBIGUOUS_VALUE). If the cursor covers a homogeneous region, or is collapsed, then this is true. */ private static boolean isPropertyDefault(XTextCursor cursor, String propertyName) - throws - UnknownPropertyException { + throws + UnknownPropertyException { XPropertyState xPropertyState = UnoCast.cast(XPropertyState.class, cursor).get(); PropertyState state = xPropertyState.getPropertyState(propertyName); if (state == PropertyState.AMBIGUOUS_VALUE) { throw new java.lang.IllegalArgumentException("PropertyState.AMBIGUOUS_VALUE" - + " (expected properties for a homogeneous cursor)"); + + " (expected properties for a homogeneous cursor)"); } return state == PropertyState.DEFAULT_VALUE; } @@ -657,33 +618,33 @@ private static boolean isPropertyDefault(XTextCursor cursor, String propertyName private static List> setCharWeight(float value) { List> settings = new ArrayList<>(); - settings.add(new OOPair<>("CharWeight", (Float) value)); + settings.add(new OOPair<>("CharWeight", value)); return settings; } private static List> setCharPosture(FontSlant value) { List> settings = new ArrayList<>(); - settings.add(new OOPair<>("CharPosture", (Object) value)); + settings.add(new OOPair<>("CharPosture", value)); return settings; } private static List> setCharCaseMap(short value) { List> settings = new ArrayList<>(); - settings.add(new OOPair<>("CharCaseMap", (Short) value)); + settings.add(new OOPair<>("CharCaseMap", value)); return settings; } // com.sun.star.awt.FontUnderline private static List> setCharUnderline(short value) { List> settings = new ArrayList<>(); - settings.add(new OOPair<>(CHAR_UNDERLINE, (Short) value)); + settings.add(new OOPair<>(CHAR_UNDERLINE, value)); return settings; } // com.sun.star.awt.FontStrikeout private static List> setCharStrikeout(short value) { List> settings = new ArrayList<>(); - settings.add(new OOPair<>(CHAR_STRIKEOUT, (Short) value)); + settings.add(new OOPair<>(CHAR_STRIKEOUT, value)); return settings; } @@ -701,7 +662,7 @@ private static List> setCharStyleName(String value) { // Locale private static List> setCharLocale(Locale value) { List> settings = new ArrayList<>(); - settings.add(new OOPair<>("CharLocale", (Object) value)); + settings.add(new OOPair<>("CharLocale", value)); return settings; } @@ -731,12 +692,12 @@ private static List> setCharEscapement(Optional va MyPropertyStack formatStack) { List> settings = new ArrayList<>(); Optional oldValue = (formatStack - .getPropertyValue(CHAR_ESCAPEMENT) - .map(e -> (short) e)); + .getPropertyValue(CHAR_ESCAPEMENT) + .map(e -> (short) e)); Optional oldHeight = (formatStack - .getPropertyValue(CHAR_ESCAPEMENT_HEIGHT) - .map(e -> (byte) e)); + .getPropertyValue(CHAR_ESCAPEMENT_HEIGHT) + .map(e -> (byte) e)); if (relative && (value.isPresent() || height.isPresent())) { double oldHeightFloat = oldHeight.orElse(CHAR_ESCAPEMENT_HEIGHT_DEFAULT) * 0.01; @@ -746,17 +707,17 @@ private static List> setCharEscapement(Optional va byte newHeight = (byte) Math.round(heightFloat * oldHeightFloat); short newValue = (short) Math.round(valueFloat * oldHeightFloat + oldValueFloat); if (value.isPresent()) { - settings.add(new OOPair<>(CHAR_ESCAPEMENT, (Short) newValue)); + settings.add(new OOPair<>(CHAR_ESCAPEMENT, newValue)); } if (height.isPresent()) { - settings.add(new OOPair<>(CHAR_ESCAPEMENT_HEIGHT, (Byte) newHeight)); + settings.add(new OOPair<>(CHAR_ESCAPEMENT_HEIGHT, newHeight)); } } else { if (value.isPresent()) { - settings.add(new OOPair<>(CHAR_ESCAPEMENT, (Short) value.get())); + settings.add(new OOPair<>(CHAR_ESCAPEMENT, value.get())); } if (height.isPresent()) { - settings.add(new OOPair<>(CHAR_ESCAPEMENT_HEIGHT, (Byte) height.get())); + settings.add(new OOPair<>(CHAR_ESCAPEMENT_HEIGHT, height.get())); } } return settings; @@ -764,19 +725,19 @@ private static List> setCharEscapement(Optional va private static List> setSubScript(MyPropertyStack formatStack) { return setCharEscapement(Optional.of(SUBSCRIPT_VALUE), - Optional.of(SUBSCRIPT_HEIGHT), - true, - formatStack); + Optional.of(SUBSCRIPT_HEIGHT), + true, + formatStack); } private static List> setSuperScript(MyPropertyStack formatStack) { return setCharEscapement(Optional.of(SUPERSCRIPT_VALUE), - Optional.of(SUPERSCRIPT_HEIGHT), - true, - formatStack); + Optional.of(SUPERSCRIPT_HEIGHT), + true, + formatStack); } - /* + /** * @return true on failure */ public static boolean setParagraphStyle(XTextCursor cursor, String paragraphStyle) { @@ -789,9 +750,9 @@ public static boolean setParagraphStyle(XTextCursor cursor, String paragraphStyl propertySet.setPropertyValue(PARA_STYLE_NAME, paragraphStyle); return PASS; } catch (UnknownPropertyException - | PropertyVetoException - | com.sun.star.lang.IllegalArgumentException - | WrappedTargetException ex) { + | PropertyVetoException + | com.sun.star.lang.IllegalArgumentException + | WrappedTargetException ex) { return FAIL; } } @@ -806,5 +767,4 @@ private static void insertParagraphBreak(XText text, XTextCursor cursor) { throw new java.lang.IllegalArgumentException("Caught unexpected com.sun.star.lang.IllegalArgumentException", ex); } } - } diff --git a/src/main/java/org/jabref/model/openoffice/rangesort/FunctionalTextViewCursor.java b/src/main/java/org/jabref/model/openoffice/rangesort/FunctionalTextViewCursor.java index e08a7f4a2ad..314de5e37f3 100644 --- a/src/main/java/org/jabref/model/openoffice/rangesort/FunctionalTextViewCursor.java +++ b/src/main/java/org/jabref/model/openoffice/rangesort/FunctionalTextViewCursor.java @@ -44,13 +44,13 @@ public class FunctionalTextViewCursor { /* The initial position of the cursor or null. */ - private XTextRange initialPosition; + private final XTextRange initialPosition; /* The initial selection in the document or null. */ - private XServiceInfo initialSelection; + private final XServiceInfo initialSelection; /* The view cursor, potentially moved from its original location. */ - private XTextViewCursor viewCursor; + private final XTextViewCursor viewCursor; private FunctionalTextViewCursor(XTextRange initialPosition, XServiceInfo initialSelection, @@ -90,8 +90,8 @@ public static OOResult get(XTextDocument doc) if (initialSelection == null) { String errorMessage = ("Selection is not available: cannot provide a functional view cursor"); return OOResult.error(errorMessage); - } else if (!Arrays.stream(initialSelection.getSupportedServiceNames()) - .anyMatch("com.sun.star.text.TextRanges"::equals)) { + } else if (Arrays.stream(initialSelection.getSupportedServiceNames()) + .noneMatch("com.sun.star.text.TextRanges"::equals)) { // initialSelection does not support TextRanges. // We need to change it (and the viewCursor with it). XTextRange newSelection = doc.getText().getStart(); diff --git a/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlap.java b/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlap.java index 3edd059d7ff..1f4f3ae0ed4 100644 --- a/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlap.java +++ b/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlap.java @@ -3,7 +3,7 @@ import java.util.List; /** - * Used in reporting range overlaps. + * Used in reporting range overlaps. */ public class RangeOverlap { public final RangeOverlapKind kind; diff --git a/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapBetween.java b/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapBetween.java index 16b7735fb7a..a5cb13fb4f0 100644 --- a/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapBetween.java +++ b/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapBetween.java @@ -14,13 +14,14 @@ public class RangeOverlapBetween { - private RangeOverlapBetween() { } + private RangeOverlapBetween() { + } /** * Check for any overlap between two sets of XTextRange values. - * + *

* Assume fewHolders is small (usually a single element, for checking the cursor) - * + *

* Returns on first problem found. */ public static @@ -44,8 +45,8 @@ List> findFirst(XTextDocument doc, for (V aHolder : fewHolders) { XText aText = aHolder.getRange().getText(); fewTuples.add(new OOTuple3<>(aText, - UnoCast.cast(XTextRangeCompare.class, aText).get(), - aHolder)); + UnoCast.cast(XTextRangeCompare.class, aText).get(), + aHolder)); } /* @@ -81,14 +82,14 @@ List> findFirst(XTextDocument doc, // // We return EQUAL_RANGE RangeOverlapKind kind = (equal ? RangeOverlapKind.EQUAL_RANGE - : (touching ? RangeOverlapKind.TOUCH - : RangeOverlapKind.OVERLAP)); + : (touching ? RangeOverlapKind.TOUCH + : RangeOverlapKind.OVERLAP)); List valuesForOverlappingRanges = new ArrayList<>(); valuesForOverlappingRanges.add(aHolder); valuesForOverlappingRanges.add(bHolder); - result.add(new RangeOverlap(kind, valuesForOverlappingRanges)); + result.add(new RangeOverlap<>(kind, valuesForOverlappingRanges)); return result; } } diff --git a/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapKind.java b/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapKind.java index 2bb7f8f4af7..1098a85a87f 100644 --- a/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapKind.java +++ b/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapKind.java @@ -2,13 +2,19 @@ public enum RangeOverlapKind { - /** The ranges share a boundary */ + /** + * The ranges share a boundary + */ TOUCH, - /** They share some characters */ + /** + * They share some characters + */ OVERLAP, - /** They cover the same XTextRange */ + /** + * They cover the same XTextRange + */ EQUAL_RANGE } diff --git a/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapWithin.java b/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapWithin.java index a8534b630ae..69ff4ca91d1 100644 --- a/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapWithin.java +++ b/src/main/java/org/jabref/model/openoffice/rangesort/RangeOverlapWithin.java @@ -12,23 +12,18 @@ public class RangeOverlapWithin { - private RangeOverlapWithin() { } + private RangeOverlapWithin() { + } /** * Report identical, overlapping or touching ranges between elements of rangeHolders. + *

+ * For overlapping and touching, only report consecutive ranges and only with a single sample of otherwise identical ranges. * - * For overlapping and touching, only report consecutive ranges and only with a single sample of - * otherwise identical ranges. - * - * @param rangeHolders represent the ranges to be checked. - * - * Note: for each rangeHolder, rangeHolder.getRange() is called multiple times. - * To avoid repeated work, they should keep a copy of the range instead of - * getting it each time from the document. - * - * @param reportAtMost Limit the number of records returned to atMost. - * Zero {@code reportAtMost} means no limit. - * + * @param rangeHolders represent the ranges to be checked. + *

+ * Note: for each rangeHolder, rangeHolder.getRange() is called multiple times. To avoid repeated work, they should keep a copy of the range instead of getting it each time from the document. + * @param reportAtMost Limit the number of records returned to atMost. Zero {@code reportAtMost} means no limit. * @param includeTouching Should the result contain ranges sharing only a boundary? */ public static @@ -44,13 +39,10 @@ List> findOverlappingRanges(XTextDocument doc, /** * Report identical, overlapping or touching ranges. + *

+ * For overlapping and touching, only report consecutive ranges and only with a single sample of otherwise identical ranges. * - * For overlapping and touching, only report consecutive ranges and only with a single sample of - * otherwise identical ranges. - * - * @param atMost Limit the number of records returned to atMost. - * Zero {@code atMost} means no limit. - * + * @param atMost Limit the number of records returned to atMost. Zero {@code atMost} means no limit. * @param includeTouching Should the result contain ranges sharing only a boundary? */ public static @@ -66,7 +58,7 @@ List> findOverlappingRanges(RangeSort.RangePartitions input, continue; } XTextRangeCompare cmp = UnoCast.cast(XTextRangeCompare.class, - partition.get(0).getRange().getText()).get(); + partition.get(0).getRange().getText()).get(); for (int i = 0; i < (partition.size() - 1); i++) { V aHolder = partition.get(i); @@ -82,15 +74,15 @@ List> findOverlappingRanges(RangeSort.RangePartitions input, // aValues.add(bHolder); // collect those equal while (i < (partition.size() - 1) && - UnoTextRange.compareStartsThenEndsUnsafe( - cmp, - aRange, - partition.get(i + 1).getRange()) == 0) { + UnoTextRange.compareStartsThenEndsUnsafe( + cmp, + aRange, + partition.get(i + 1).getRange()) == 0) { bHolder = partition.get(i + 1); aValues.add(bHolder); i++; } - result.add(new RangeOverlap(RangeOverlapKind.EQUAL_RANGE, aValues)); + result.add(new RangeOverlap<>(RangeOverlapKind.EQUAL_RANGE, aValues)); if (atMost > 0 && result.size() >= atMost) { return result; } @@ -105,17 +97,16 @@ List> findOverlappingRanges(RangeSort.RangePartitions input, List valuesForOverlappingRanges = new ArrayList<>(); valuesForOverlappingRanges.add(aHolder); valuesForOverlappingRanges.add(bHolder); - result.add(new RangeOverlap((cmpResult == 0) - ? RangeOverlapKind.TOUCH - : RangeOverlapKind.OVERLAP, - valuesForOverlappingRanges)); + result.add(new RangeOverlap<>((cmpResult == 0) + ? RangeOverlapKind.TOUCH + : RangeOverlapKind.OVERLAP, + valuesForOverlappingRanges)); } if (atMost > 0 && result.size() >= atMost) { - return result; + return result; } } } return result; } - } diff --git a/src/main/java/org/jabref/model/openoffice/rangesort/RangeSort.java b/src/main/java/org/jabref/model/openoffice/rangesort/RangeSort.java index 99ace5154e1..77982a9a7db 100644 --- a/src/main/java/org/jabref/model/openoffice/rangesort/RangeSort.java +++ b/src/main/java/org/jabref/model/openoffice/rangesort/RangeSort.java @@ -13,22 +13,17 @@ import com.sun.star.text.XTextRangeCompare; /** - * RangeSort provides sorting based on XTextRangeCompare, which only provides comparison - * between XTextRange values within the same XText. + * RangeSort provides sorting based on XTextRangeCompare, which only provides comparison between XTextRange values within the same XText. */ public class RangeSort { private RangeSort() { - /**/ } /** * Compare two RangeHolders (using RangeHolder.getRange()) within an XText. - * - * Note: since we only look at the ranges, this comparison is generally not consistent with - * `equals` on the RangeHolders. Probably should not be used for key comparison in - * {@code TreeMap} or {@code Set} - * + *

+ * Note: since we only look at the ranges, this comparison is generally not consistent with `equals` on the RangeHolders. Probably should not be used for key comparison in {@code TreeMap} or {@code Set} */ private static class HolderComparatorWithinPartition implements Comparator { @@ -36,7 +31,7 @@ private static class HolderComparatorWithinPartition implements Comparator * Note: RangeHolder.getRange() is called many times. */ public static void sortWithinPartition(List rangeHolders) { @@ -83,7 +78,7 @@ public List> getPartitions() { } /** - * Partition RangeHolders by the corresponding XText. + * Partition RangeHolders by the corresponding XText. */ public static RangePartitions partitionRanges(List holders) { RangePartitions result = new RangePartitions<>(); diff --git a/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortEntry.java b/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortEntry.java index 0ed686ee901..a9b834027ff 100644 --- a/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortEntry.java +++ b/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortEntry.java @@ -9,7 +9,7 @@ public class RangeSortEntry implements RangeSortable { private XTextRange range; private int indexInPosition; - private T content; + private final T content; public RangeSortEntry(XTextRange range, int indexInPosition, T content) { this.range = range; diff --git a/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortVisual.java b/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortVisual.java index e412f4ade6c..b83db3f1555 100644 --- a/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortVisual.java +++ b/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortVisual.java @@ -14,25 +14,21 @@ /** * Sort XTextRange values visually (top-down,left-to-right). - * + *

* Requires functional XTextViewCursor. - * - * Problem: for multicolumn layout and when viewing pages side-by-side in LO, the - * (top-down,left-to-right) order interpreted as-on-the-screen: an XTextRange at the top of - * the second column or second page is sorted before an XTextRange at the bottom of the - * first column of the first page. + *

+ * Problem: for multicolumn layout and when viewing pages side-by-side in LO, the (top-down,left-to-right) order interpreted as-on-the-screen: an XTextRange at the top of the second column or second page is sorted before an XTextRange at the bottom of the first column of the first page. */ public class RangeSortVisual { private static final Logger LOGGER = LoggerFactory.getLogger(RangeSortVisual.class); private RangeSortVisual() { - /**/ } /** * Sort the input {@code inputs} visually. - * + *

* Requires a functional {@code XTextViewCursor}. * * @return The input, sorted by the elements XTextRange and getIndexInPosition. @@ -63,8 +59,8 @@ public static List> visualSort(List> input for (int i = 0; i < inputSize; i++) { RangeSortable input = inputs.get(i); comparableMarks.add(new ComparableMark<>(positions.get(i), - input.getIndexInPosition(), - input)); + input.getIndexInPosition(), + input)); } comparableMarks.sort(RangeSortVisual::compareTopToBottomLeftToRight); @@ -82,22 +78,16 @@ public static List> visualSort(List> input } /** - * Given a location, return its position: coordinates relative to the top left position of the - * first page of the document. - * - * Note: for text layouts with two or more columns, this gives the wrong order: - * top-down/left-to-right does not match reading order. - * - * Note: The "relative to the top left position of the first page" is meant "as it appears on - * the screen". - * - * In particular: when viewing pages side-by-side, the top half of the right page is - * higher than the lower half of the left page. Again, top-down/left-to-right does not - * match reading order. + * Given a location, return its position: coordinates relative to the top left position of the first page of the document. + *

+ * Note: for text layouts with two or more columns, this gives the wrong order: top-down/left-to-right does not match reading order. + *

+ * Note: The "relative to the top left position of the first page" is meant "as it appears on the screen". + *

+ * In particular: when viewing pages side-by-side, the top half of the right page is higher than the lower half of the left page. Again, top-down/left-to-right does not match reading order. * * @param range Location. - * @param cursor To get the position, we need az XTextViewCursor. - * It will be moved to the range. + * @param cursor To get the position, we need az XTextViewCursor. It will be moved to the range. */ private static Point findPositionOfTextRange(XTextRange range, XTextViewCursor cursor) { cursor.gotoRange(range, false); @@ -117,10 +107,9 @@ private static int compareTopToBottomLeftToRight(ComparableMark a, Compar /** * A reference mark name paired with its visual position. - * - * Comparison is based on (Y,X,indexInPosition): vertical compared first, horizontal second, - * indexInPosition third. - * + *

+ * Comparison is based on (Y,X,indexInPosition): vertical compared first, horizontal second, indexInPosition third. + *

* Used for sorting reference marks by their visual positions. */ private static class ComparableMark { @@ -138,7 +127,5 @@ public ComparableMark(Point position, int indexInPosition, T content) { public T getContent() { return content; } - } - } diff --git a/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortable.java b/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortable.java index 59a4c3fa9af..15482f0a3e6 100644 --- a/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortable.java +++ b/src/main/java/org/jabref/model/openoffice/rangesort/RangeSortable.java @@ -7,9 +7,10 @@ */ public interface RangeSortable extends RangeHolder { - /** The XTextRange - * - * For citation marks in footnotes this may be the range of the footnote mark. + /** + * The XTextRange + *

+ * For citation marks in footnotes this may be the range of the footnote mark. */ XTextRange getRange(); diff --git a/src/main/java/org/jabref/model/openoffice/style/Citation.java b/src/main/java/org/jabref/model/openoffice/style/Citation.java index 7bae4fd2548..899013773f2 100644 --- a/src/main/java/org/jabref/model/openoffice/style/Citation.java +++ b/src/main/java/org/jabref/model/openoffice/style/Citation.java @@ -10,27 +10,36 @@ public class Citation implements ComparableCitation, CitationMarkerEntry, CitationMarkerNumericEntry { - /** key in database */ + /** + * key in database + */ public final String citationKey; - /** Result from database lookup. Optional.empty() if not found. */ + /** + * Result from database lookup. Optional.empty() if not found. + */ private Optional db; - /** The number used for numbered citation styles . */ + /** + * The number used for numbered citation styles . + */ private Optional number; - /** Letter that makes the in-text citation unique. */ + /** + * Letter that makes the in-text citation unique. + */ private Optional uniqueLetter; - /** pageInfo */ + /** + * pageInfo + */ private Optional pageInfo; - /** isFirstAppearanceOfSource */ - private boolean isFirstAppearanceOfSource; - /** - * + * isFirstAppearanceOfSource */ + private boolean isFirstAppearanceOfSource; + public Citation(String citationKey) { this.citationKey = citationKey; this.db = Optional.empty(); @@ -57,9 +66,7 @@ public boolean getIsFirstAppearanceOfSource() { @Override public Optional getBibEntry() { - return (db.isPresent() - ? Optional.of(db.get().entry) - : Optional.empty()); + return db.map(citationLookupResult -> citationLookupResult.entry); } public static Optional lookup(BibDatabase database, String key) { @@ -70,10 +77,10 @@ public static Optional lookup(BibDatabase database, String public static Optional lookup(List databases, String key) { return (databases.stream() - .map(database -> Citation.lookup(database, key)) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst()); + .map(database -> Citation.lookup(database, key)) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst()); } public void lookupInDatabases(List databases) { @@ -129,18 +136,17 @@ public void setIsFirstAppearanceOfSource(boolean value) { * Setters for CitationGroups.distribute() */ public static void setLookupResult(OOPair> pair) { - Citation cit = pair.a; - cit.db = pair.b; + Citation citation = pair.a; + citation.db = pair.b; } public static void setNumber(OOPair> pair) { - Citation cit = pair.a; - cit.number = pair.b; + Citation citation = pair.a; + citation.number = pair.b; } public static void setUniqueLetter(OOPair> pair) { - Citation cit = pair.a; - cit.uniqueLetter = pair.b; + Citation citation = pair.a; + citation.uniqueLetter = pair.b; } - } diff --git a/src/main/java/org/jabref/model/openoffice/style/CitationGroup.java b/src/main/java/org/jabref/model/openoffice/style/CitationGroup.java index 90afb872d4f..30cfbdd512e 100644 --- a/src/main/java/org/jabref/model/openoffice/style/CitationGroup.java +++ b/src/main/java/org/jabref/model/openoffice/style/CitationGroup.java @@ -28,10 +28,6 @@ public class CitationGroup { public final CitationType citationType; public final List citationsInStorageOrder; - /* - * Extra data - */ - /* * A name of a reference mark to link to by formatCitedOnPages. * May be initially empty, if backend does not use reference marks. @@ -98,7 +94,7 @@ void imposeLocalOrder(Comparator entryComparator) { } this.localOrder = OOListUtil.order(citationsInStorageOrder, - new CompareCitation(entryComparator, true)); + new CompareCitation(entryComparator, true)); if (dataModel == OODataModel.JabRef52) { getCitationsInLocalOrder().get(last).setPageInfo(lastPageInfo); @@ -114,7 +110,7 @@ public List getLocalOrder() { */ public List getCitationsInLocalOrder() { - return OOListUtil.map(localOrder, i -> citationsInStorageOrder.get(i)); + return OOListUtil.map(localOrder, citationsInStorageOrder::get); } /* @@ -152,5 +148,4 @@ public void setCitationMarker(Optional citationMarker) { public Optional getCitationMarker() { return this.citationMarker; } - } diff --git a/src/main/java/org/jabref/model/openoffice/style/CitationGroupId.java b/src/main/java/org/jabref/model/openoffice/style/CitationGroupId.java index baaa931214c..6c271b12267 100644 --- a/src/main/java/org/jabref/model/openoffice/style/CitationGroupId.java +++ b/src/main/java/org/jabref/model/openoffice/style/CitationGroupId.java @@ -5,6 +5,7 @@ */ public class CitationGroupId { String groupId; + public CitationGroupId(String groupId) { this.groupId = groupId; } diff --git a/src/main/java/org/jabref/model/openoffice/style/CitationGroups.java b/src/main/java/org/jabref/model/openoffice/style/CitationGroups.java index e5e642fa652..68dcd714974 100644 --- a/src/main/java/org/jabref/model/openoffice/style/CitationGroups.java +++ b/src/main/java/org/jabref/model/openoffice/style/CitationGroups.java @@ -19,15 +19,14 @@ /** * CitationGroups : the set of citation groups in the document. - * + *

* This is the main input (as well as output) for creating citation markers and bibliography. - * */ public class CitationGroups { private static final Logger LOGGER = LoggerFactory.getLogger(CitationGroups.class); - private Map citationGroupsUnordered; + private final Map citationGroupsUnordered; /** * Provides order of appearance for the citation groups. @@ -35,7 +34,7 @@ public class CitationGroups { private Optional> globalOrder; /** - * This is going to be the bibliography + * This is going to be the bibliography */ private Optional bibliography; @@ -114,13 +113,12 @@ public List getCitationGroupsInGlobalOrder() { if (globalOrder.isEmpty()) { throw new IllegalStateException("getCitationGroupsInGlobalOrder: not ordered yet"); } - return OOListUtil.map(globalOrder.get(), groupId -> citationGroupsUnordered.get(groupId)); + return OOListUtil.map(globalOrder.get(), citationGroupsUnordered::get); } /** - * Impose an order of citation groups by providing the order of their citation group - * idendifiers. - * + * Impose an order of citation groups by providing the order of their citation group idendifiers. + *

* Also set indexInGlobalOrder for each citation group. */ public void setGlobalOrder(List globalOrder) { @@ -151,8 +149,7 @@ public void imposeLocalOrder(Comparator entryComparator) { } /** - * Collect citations into a list of cited sources using neither CitationGroup.globalOrder or - * Citation.localOrder + * Collect citations into a list of cited sources using neither CitationGroup.globalOrder or Citation.localOrder */ public CitedKeys getCitedKeysUnordered() { LinkedHashMap res = new LinkedHashMap<>(); @@ -216,9 +213,9 @@ public List getUnresolvedKeys() { } public void createNumberedBibliographySortedInOrderOfAppearance() { - if (!bibliography.isEmpty()) { + if (bibliography.isPresent()) { throw new IllegalStateException("createNumberedBibliographySortedInOrderOfAppearance:" - + " already have a bibliography"); + + " already have a bibliography"); } CitedKeys citedKeys = getCitedKeysSortedInOrderOfAppearance(); citedKeys.numberCitedKeysInCurrentOrder(); @@ -230,7 +227,7 @@ public void createNumberedBibliographySortedInOrderOfAppearance() { * precondition: database lookup already performed (otherwise we just sort citation keys) */ public void createPlainBibliographySortedByComparator(Comparator entryComparator) { - if (!bibliography.isEmpty()) { + if (bibliography.isPresent()) { throw new IllegalStateException("createPlainBibliographySortedByComparator: already have a bibliography"); } CitedKeys citedKeys = getCitedKeysUnordered(); @@ -242,7 +239,7 @@ public void createPlainBibliographySortedByComparator(Comparator entry * precondition: database lookup already performed (otherwise we just sort citation keys) */ public void createNumberedBibliographySortedByComparator(Comparator entryComparator) { - if (!bibliography.isEmpty()) { + if (bibliography.isPresent()) { throw new IllegalStateException("createNumberedBibliographySortedByComparator: already have a bibliography"); } CitedKeys citedKeys = getCitedKeysUnordered(); @@ -261,7 +258,7 @@ public Optional getCitationGroup(CitationGroupId groupId) { return Optional.ofNullable(group); } - /* + /** * @return true if all citation groups have referenceMarkNameForLinking */ public boolean citationGroupsProvideReferenceMarkNameForLinking() { @@ -290,5 +287,4 @@ public void afterRemoveCitationGroup(CitationGroup group) { bibliography = Optional.empty(); } - } diff --git a/src/main/java/org/jabref/model/openoffice/style/CitationLookupResult.java b/src/main/java/org/jabref/model/openoffice/style/CitationLookupResult.java index bfe6dc1debe..c82177a7e69 100644 --- a/src/main/java/org/jabref/model/openoffice/style/CitationLookupResult.java +++ b/src/main/java/org/jabref/model/openoffice/style/CitationLookupResult.java @@ -19,16 +19,12 @@ public CitationLookupResult(BibEntry entry, BibDatabase database) { /** * Note: BibEntry overrides Object.equals, but BibDatabase does not. - * - * Consequently, {@code this.database.equals(that.database)} below - * is equivalent to {@code this.database == that.database}. - * - * Since within each GUI call we use a fixed list of databases, it is OK. - * - * CitationLookupResult.equals is used in CitedKey.addPath to check the added Citation - * refers to the same source as the others. As long as we look up each citation key - * only once (in CitationGroups.lookupCitations), the default implementation for equals - * would be sufficient (and could also omit hashCode below). + *

+ * Consequently, {@code this.database.equals(that.database)} below is equivalent to {@code this.database == that.database}. + *

+ * Since within each GUI call we use a fixed list of databases, it is OK. + *

+ * CitationLookupResult.equals is used in CitedKey.addPath to check the added Citation refers to the same source as the others. As long as we look up each citation key only once (in CitationGroups.lookupCitations), the default implementation for equals would be sufficient (and could also omit hashCode below). */ @Override public boolean equals(Object otherObject) { diff --git a/src/main/java/org/jabref/model/openoffice/style/CitationMarkerEntry.java b/src/main/java/org/jabref/model/openoffice/style/CitationMarkerEntry.java index 5fcb9bcc1a8..18bbe5588b3 100644 --- a/src/main/java/org/jabref/model/openoffice/style/CitationMarkerEntry.java +++ b/src/main/java/org/jabref/model/openoffice/style/CitationMarkerEntry.java @@ -15,14 +15,12 @@ public interface CitationMarkerEntry extends CitationMarkerNormEntry { Optional getUniqueLetter(); /** - * pageInfo for this citation, provided by the user. - * May be empty, for none. + * pageInfo for this citation, provided by the user. May be empty, for none. */ Optional getPageInfo(); /** - * @return true if this citation is the first appearance of the source cited. Some styles use - * different limit on the number of authors shown in this case. + * @return true if this citation is the first appearance of the source cited. Some styles use different limit on the number of authors shown in this case. */ boolean getIsFirstAppearanceOfSource(); } diff --git a/src/main/java/org/jabref/model/openoffice/style/CitationMarkerNormEntry.java b/src/main/java/org/jabref/model/openoffice/style/CitationMarkerNormEntry.java index a2581b38b60..1489dede932 100644 --- a/src/main/java/org/jabref/model/openoffice/style/CitationMarkerNormEntry.java +++ b/src/main/java/org/jabref/model/openoffice/style/CitationMarkerNormEntry.java @@ -7,14 +7,16 @@ */ public interface CitationMarkerNormEntry { - /** Citation key. This is what we usually get from the document. - * - * Used if getLookupResult() returns empty, which indicates failure to lookup in the databases. + /** + * Citation key. This is what we usually get from the document. + *

+ * Used if getLookupResult() returns empty, which indicates failure to lookup in the databases. */ String getCitationKey(); - /** Result of looking up citation key in databases. - * + /** + * Result of looking up citation key in databases. + *

* Optional.empty() indicates unresolved citation. */ Optional getLookupResult(); diff --git a/src/main/java/org/jabref/model/openoffice/style/CitationPath.java b/src/main/java/org/jabref/model/openoffice/style/CitationPath.java index 0920ea20feb..ba562ebe6bf 100644 --- a/src/main/java/org/jabref/model/openoffice/style/CitationPath.java +++ b/src/main/java/org/jabref/model/openoffice/style/CitationPath.java @@ -1,8 +1,7 @@ package org.jabref.model.openoffice.style; /** - * Identifies a citation with the identifier of the citation group containing it and its storage - * index within. + * Identifies a citation with the identifier of the citation group containing it and its storage index within. */ public class CitationPath { diff --git a/src/main/java/org/jabref/model/openoffice/style/CitedKey.java b/src/main/java/org/jabref/model/openoffice/style/CitedKey.java index 5331204bd75..cd3dc74684e 100644 --- a/src/main/java/org/jabref/model/openoffice/style/CitedKey.java +++ b/src/main/java/org/jabref/model/openoffice/style/CitedKey.java @@ -10,14 +10,13 @@ /** * Cited keys are collected from the citations in citation groups. - * - * They contain backreferences to the corresponding citations in {@code where}. This allows the - * extra information generated using CitedKeys to be distributed back to the in-text citations. + *

+ * They contain backreferences to the corresponding citations in {@code where}. This allows the extra information generated using CitedKeys to be distributed back to the in-text citations. */ public class CitedKey implements - ComparableCitedKey, - CitationMarkerNormEntry, - CitationMarkerNumericBibEntry { + ComparableCitedKey, + CitationMarkerNormEntry, + CitationMarkerNumericBibEntry { public final String citationKey; private final List where; @@ -27,16 +26,16 @@ public class CitedKey implements private Optional uniqueLetter; // For AuthorYear citation styles. private Optional normCitMarker; // For AuthorYear citation styles. - CitedKey(String citationKey, CitationPath path, Citation cit) { + CitedKey(String citationKey, CitationPath path, Citation citation) { this.citationKey = citationKey; this.where = new ArrayList<>(); // remember order this.where.add(path); // synchronized with Citation - this.db = cit.getLookupResult(); - this.number = cit.getNumber(); - this.uniqueLetter = cit.getUniqueLetter(); + this.db = citation.getLookupResult(); + this.number = citation.getNumber(); + this.uniqueLetter = citation.getUniqueLetter(); // CitedKey only this.normCitMarker = Optional.empty(); @@ -120,19 +119,19 @@ void lookupInDatabases(List databases) { this.db = Citation.lookup(databases, this.citationKey); } - void distributeLookupResult(CitationGroups cgs) { - cgs.distributeToCitations(where, Citation::setLookupResult, db); + void distributeLookupResult(CitationGroups citationGroups) { + citationGroups.distributeToCitations(where, Citation::setLookupResult, db); } /* * Make unique using a letter or by numbering */ - void distributeNumber(CitationGroups cgs) { - cgs.distributeToCitations(where, Citation::setNumber, number); + void distributeNumber(CitationGroups citationGroups) { + citationGroups.distributeToCitations(where, Citation::setNumber, number); } - void distributeUniqueLetter(CitationGroups cgs) { - cgs.distributeToCitations(where, Citation::setUniqueLetter, uniqueLetter); + void distributeUniqueLetter(CitationGroups citationGroups) { + citationGroups.distributeToCitations(where, Citation::setUniqueLetter, uniqueLetter); } } diff --git a/src/main/java/org/jabref/model/openoffice/style/CitedKeys.java b/src/main/java/org/jabref/model/openoffice/style/CitedKeys.java index 059c1b6123a..7b95d1ece19 100644 --- a/src/main/java/org/jabref/model/openoffice/style/CitedKeys.java +++ b/src/main/java/org/jabref/model/openoffice/style/CitedKeys.java @@ -63,21 +63,21 @@ public void lookupInDatabases(List databases) { } } - void distributeLookupResults(CitationGroups cgs) { + void distributeLookupResults(CitationGroups citationGroups) { for (CitedKey ck : this.data.values()) { - ck.distributeLookupResult(cgs); + ck.distributeLookupResult(citationGroups); } } - void distributeNumbers(CitationGroups cgs) { + void distributeNumbers(CitationGroups citationGroups) { for (CitedKey ck : this.data.values()) { - ck.distributeNumber(cgs); + ck.distributeNumber(citationGroups); } } - public void distributeUniqueLetters(CitationGroups cgs) { + public void distributeUniqueLetters(CitationGroups citationGroups) { for (CitedKey ck : this.data.values()) { - ck.distributeUniqueLetter(cgs); + ck.distributeUniqueLetter(citationGroups); } } diff --git a/src/main/java/org/jabref/model/openoffice/style/ComparableCitation.java b/src/main/java/org/jabref/model/openoffice/style/ComparableCitation.java index 81120c09b2a..42a7e5869ba 100644 --- a/src/main/java/org/jabref/model/openoffice/style/ComparableCitation.java +++ b/src/main/java/org/jabref/model/openoffice/style/ComparableCitation.java @@ -5,8 +5,7 @@ import org.jabref.model.openoffice.ootext.OOText; /** - * When sorting citations (in a group), we also consider pageInfo. - * Otherwise we sort citations as cited keys. + * When sorting citations (in a group), we also consider pageInfo. Otherwise we sort citations as cited keys. */ public interface ComparableCitation extends ComparableCitedKey { Optional getPageInfo(); diff --git a/src/main/java/org/jabref/model/openoffice/style/CompareCitation.java b/src/main/java/org/jabref/model/openoffice/style/CompareCitation.java index 966715b11a8..1d27674ba1d 100644 --- a/src/main/java/org/jabref/model/openoffice/style/CompareCitation.java +++ b/src/main/java/org/jabref/model/openoffice/style/CompareCitation.java @@ -10,7 +10,7 @@ */ public class CompareCitation implements Comparator { - private CompareCitedKey citedKeyComparator; + private final CompareCitedKey citedKeyComparator; CompareCitation(Comparator entryComparator, boolean unresolvedComesFirst) { this.citedKeyComparator = new CompareCitedKey(entryComparator, unresolvedComesFirst); diff --git a/src/main/java/org/jabref/model/openoffice/style/NonUniqueCitationMarker.java b/src/main/java/org/jabref/model/openoffice/style/NonUniqueCitationMarker.java index 34300734d2f..e633f7a9c70 100644 --- a/src/main/java/org/jabref/model/openoffice/style/NonUniqueCitationMarker.java +++ b/src/main/java/org/jabref/model/openoffice/style/NonUniqueCitationMarker.java @@ -1,15 +1,18 @@ package org.jabref.model.openoffice.style; /** - * What should createCitationMarker do if it discovers that uniqueLetters provided are not - * sufficient for unique presentation? + * What should createCitationMarker do if it discovers that uniqueLetters provided are not sufficient for unique presentation? */ public enum NonUniqueCitationMarker { - /** Give an insufficient representation anyway. */ + /** + * Give an insufficient representation anyway. + */ FORGIVEN, - /** Throw an exception */ + /** + * Throw an exception + */ THROWS } diff --git a/src/main/java/org/jabref/model/openoffice/style/OODataModel.java b/src/main/java/org/jabref/model/openoffice/style/OODataModel.java index 835715dfca5..470edc1694d 100644 --- a/src/main/java/org/jabref/model/openoffice/style/OODataModel.java +++ b/src/main/java/org/jabref/model/openoffice/style/OODataModel.java @@ -6,13 +6,19 @@ import org.jabref.model.openoffice.ootext.OOText; -/** What is the data stored? */ +/** + * What is the data stored? + */ public enum OODataModel { - /** JabRef52: pageInfo belongs to CitationGroup, not Citation. */ + /** + * JabRef52: pageInfo belongs to CitationGroup, not Citation. + */ JabRef52, - /** JabRef60: pageInfo belongs to Citation. */ + /** + * JabRef60: pageInfo belongs to Citation. + */ JabRef60; /** diff --git a/src/main/java/org/jabref/model/openoffice/style/PageInfo.java b/src/main/java/org/jabref/model/openoffice/style/PageInfo.java index 1ff87569a3b..c92ad335b49 100644 --- a/src/main/java/org/jabref/model/openoffice/style/PageInfo.java +++ b/src/main/java/org/jabref/model/openoffice/style/PageInfo.java @@ -7,7 +7,6 @@ public class PageInfo { private PageInfo() { - // hide public constructor } /* @@ -27,7 +26,7 @@ public static Optional normalizePageInfo(Optional optionalText) /** * Defines sort order for pageInfo strings. - * + *

* Optional.empty comes before non-empty. */ public static int comparePageInfo(Optional a, Optional b) { diff --git a/src/main/java/org/jabref/model/openoffice/uno/CreationException.java b/src/main/java/org/jabref/model/openoffice/uno/CreationException.java index 091d5469e2d..84e3c8afe55 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/CreationException.java +++ b/src/main/java/org/jabref/model/openoffice/uno/CreationException.java @@ -2,14 +2,12 @@ /** * Exception used to indicate failure in either - * - * XMultiServiceFactory.createInstance() - * XMultiComponentFactory.createInstanceWithContext() + *

+ * XMultiServiceFactory.createInstance() XMultiComponentFactory.createInstanceWithContext() */ public class CreationException extends Exception { public CreationException(String message) { super(message); } - } diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoBookmark.java b/src/main/java/org/jabref/model/openoffice/uno/UnoBookmark.java index fdbbb0dd1c6..f187527aeab 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoBookmark.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoBookmark.java @@ -14,14 +14,15 @@ public class UnoBookmark { - private UnoBookmark() { } + private UnoBookmark() { + } /** * Provides access to bookmarks by name. */ public static XNameAccess getNameAccess(XTextDocument doc) - throws - NoDocumentException { + throws + NoDocumentException { XBookmarksSupplier supplier = UnoCast.cast(XBookmarksSupplier.class, doc).get(); try { @@ -38,32 +39,28 @@ public static XNameAccess getNameAccess(XTextDocument doc) * @return The XTextRange for the bookmark, or Optional.empty(). */ public static Optional getAnchor(XTextDocument doc, String name) - throws - WrappedTargetException, - NoDocumentException { + throws + WrappedTargetException, + NoDocumentException { XNameAccess nameAccess = getNameAccess(doc); return (UnoNameAccess.getTextContentByName(nameAccess, name).map(XTextContent::getAnchor)); } /** - * Insert a bookmark with the given name at the cursor provided, or with another name if the one - * we asked for is already in use. - * + * Insert a bookmark with the given name at the cursor provided, or with another name if the one we asked for is already in use. + *

* In LibreOffice the another name is in "{name}{number}" format. * - * @param name For the bookmark. - * @param range Cursor marking the location or range for the bookmark. - * @param absorb Shall we incorporate range? - * + * @param name For the bookmark. + * @param range Cursor marking the location or range for the bookmark. + * @param absorb Shall we incorporate range? * @return The XNamed interface of the bookmark. - * - * result.getName() should be checked by the caller, because its name may differ from - * the one requested. + * result.getName() should be checked by the caller, because its name may differ from the one requested. */ public static XNamed create(XTextDocument doc, String name, XTextRange range, boolean absorb) - throws - CreationException { + throws + CreationException { return UnoNamed.insertNamedTextContent(doc, "com.sun.star.text.Bookmark", name, range, absorb); } @@ -71,9 +68,9 @@ public static XNamed create(XTextDocument doc, String name, XTextRange range, bo * Remove the named bookmark if it exists. */ public static void removeIfExists(XTextDocument doc, String name) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { XNameAccess marks = UnoBookmark.getNameAccess(doc); diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoCast.java b/src/main/java/org/jabref/model/openoffice/uno/UnoCast.java index fcc1bcc3e12..3234e4d130a 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoCast.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoCast.java @@ -6,7 +6,8 @@ public class UnoCast { - private UnoCast() { } + private UnoCast() { + } /** * cast : short for Optional.ofNullable(UnoRuntime.queryInterface(...)) diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoCrossRef.java b/src/main/java/org/jabref/model/openoffice/uno/UnoCrossRef.java index f5d20a1de4f..7df6d7dc04c 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoCrossRef.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoCrossRef.java @@ -14,7 +14,8 @@ public class UnoCrossRef { - private UnoCrossRef() { } + private UnoCrossRef() { + } /** * Update TextFields, etc. We use it to refresh cross-references in the document. @@ -26,17 +27,16 @@ public static void refresh(XTextDocument doc) { } /** - * Insert a clickable cross-reference to a reference mark, with a label containing the target's - * page number. - * + * Insert a clickable cross-reference to a reference mark, with a label containing the target's page number. + *

* May need a documentConnection.refresh() after, to update the text shown. */ public static void insertReferenceToPageNumberOfReferenceMark(XTextDocument doc, String referenceMarkName, XTextRange cursor) - throws - CreationException, - WrappedTargetException { + throws + CreationException, + WrappedTargetException { // based on: https://wiki.openoffice.org/wiki/Documentation/DevGuide/Text/Reference_Marks XMultiServiceFactory msf = UnoCast.cast(XMultiServiceFactory.class, doc).get(); @@ -62,20 +62,20 @@ public static void insertReferenceToPageNumberOfReferenceMark(XTextDocument doc, try { // specify that the source is a reference mark (could also be a footnote, // bookmark or sequence field) - xFieldProps.setPropertyValue("ReferenceFieldSource", Short.valueOf(ReferenceFieldSource.REFERENCE_MARK)); + xFieldProps.setPropertyValue("ReferenceFieldSource", ReferenceFieldSource.REFERENCE_MARK); } catch (UnknownPropertyException ex) { throw new java.lang.IllegalStateException("The created GetReference does not have property" - + " 'ReferenceFieldSource'"); + + " 'ReferenceFieldSource'"); } catch (PropertyVetoException ex) { throw new java.lang.IllegalStateException("Caught PropertyVetoException on 'ReferenceFieldSource'"); } try { // We want the reference displayed as page number - xFieldProps.setPropertyValue("ReferenceFieldPart", Short.valueOf(ReferenceFieldPart.PAGE)); + xFieldProps.setPropertyValue("ReferenceFieldPart", ReferenceFieldPart.PAGE); } catch (UnknownPropertyException ex) { throw new java.lang.IllegalStateException("The created GetReference does not have property" - + " 'ReferenceFieldPart'"); + + " 'ReferenceFieldPart'"); } catch (PropertyVetoException ex) { throw new java.lang.IllegalStateException("Caught PropertyVetoException on 'ReferenceFieldPart'"); } diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoCursor.java b/src/main/java/org/jabref/model/openoffice/uno/UnoCursor.java index e761a56e12f..da29ee84d92 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoCursor.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoCursor.java @@ -11,22 +11,22 @@ public class UnoCursor { - private UnoCursor() { } + private UnoCursor() { + } /** * Get the cursor positioned by the user. */ public static Optional getViewCursor(XTextDocument doc) { return (UnoTextDocument.getCurrentController(doc) - .flatMap(e -> UnoCast.cast(XTextViewCursorSupplier.class, e)) - .map(XTextViewCursorSupplier::getViewCursor)); + .flatMap(e -> UnoCast.cast(XTextViewCursorSupplier.class, e)) + .map(XTextViewCursorSupplier::getViewCursor)); } /** * Create a text cursor for a textContent. * * @return Optional.empty if mark is null, otherwise cursor. - * */ public static Optional getTextCursorOfTextContentAnchor(XTextContent mark) { if (mark == null) { diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoNameAccess.java b/src/main/java/org/jabref/model/openoffice/uno/UnoNameAccess.java index 243c136f382..1250d56d923 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoNameAccess.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoNameAccess.java @@ -9,14 +9,15 @@ public class UnoNameAccess { - private UnoNameAccess() { } + private UnoNameAccess() { + } /** * @return null if name not found, or if the result does not support the XTextContent interface. */ public static Optional getTextContentByName(XNameAccess nameAccess, String name) - throws - WrappedTargetException { + throws + WrappedTargetException { try { return UnoCast.cast(XTextContent.class, nameAccess.getByName(name)); } catch (NoSuchElementException ex) { diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoNamed.java b/src/main/java/org/jabref/model/openoffice/uno/UnoNamed.java index 5f1f50c873d..b9f43e56f80 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoNamed.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoNamed.java @@ -8,38 +8,27 @@ public class UnoNamed { - private UnoNamed() { } + private UnoNamed() { + } /** * Insert a new instance of a service at the provided cursor position. * - * @param service For example - * "com.sun.star.text.ReferenceMark", - * "com.sun.star.text.Bookmark" or - * "com.sun.star.text.TextSection". - * - * Passed to this.asXMultiServiceFactory().createInstance(service) - * The result is expected to support the XNamed and XTextContent interfaces. - * - * @param name For the ReferenceMark, Bookmark, TextSection. - * If the name is already in use, LibreOffice may change the name. - * + * @param service For example "com.sun.star.text.ReferenceMark", "com.sun.star.text.Bookmark" or "com.sun.star.text.TextSection". + *

+ * Passed to this.asXMultiServiceFactory().createInstance(service) The result is expected to support the XNamed and XTextContent interfaces. + * @param name For the ReferenceMark, Bookmark, TextSection. If the name is already in use, LibreOffice may change the name. * @param range Marks the location or range for the thing to be inserted. - * - * @param absorb ReferenceMark, Bookmark and TextSection can incorporate a text range. - * If absorb is true, the text in the range becomes part of the thing. - * If absorb is false, the thing is inserted at the end of the range. - * + * @param absorb ReferenceMark, Bookmark and TextSection can incorporate a text range. If absorb is true, the text in the range becomes part of the thing. If absorb is false, the thing is inserted at the end of the range. * @return The XNamed interface, in case we need to check the actual name. - * */ static XNamed insertNamedTextContent(XTextDocument doc, String service, String name, XTextRange range, boolean absorb) - throws - CreationException { + throws + CreationException { XMultiServiceFactory msf = UnoCast.cast(XMultiServiceFactory.class, doc).get(); @@ -51,14 +40,13 @@ static XNamed insertNamedTextContent(XTextDocument doc, } XNamed xNamed = (UnoCast.cast(XNamed.class, xObject) - .orElseThrow(() -> new IllegalArgumentException("Service is not an XNamed"))); + .orElseThrow(() -> new IllegalArgumentException("Service is not an XNamed"))); xNamed.setName(name); // get XTextContent interface XTextContent xTextContent = (UnoCast.cast(XTextContent.class, xObject) - .orElseThrow(() -> new IllegalArgumentException("Service is not an XTextContent"))); + .orElseThrow(() -> new IllegalArgumentException("Service is not an XTextContent"))); range.getText().insertTextContent(range, xTextContent, absorb); return xNamed; } - } diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoProperties.java b/src/main/java/org/jabref/model/openoffice/uno/UnoProperties.java index 3a47818cf8b..80ea0c11c80 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoProperties.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoProperties.java @@ -15,42 +15,31 @@ import com.sun.star.lang.WrappedTargetException; /** - * Utilities for properties. + * Utilities for properties. */ public class UnoProperties { - private UnoProperties() { } - - /* - * asPropertySet - */ + private UnoProperties() { + } public static Optional asPropertySet(XPropertyContainer propertyContainer) { return UnoCast.cast(XPropertySet.class, propertyContainer); } - /* - * getPropertySetInfo - */ - public static Optional getPropertySetInfo(XPropertySet propertySet) { return (Optional.ofNullable(propertySet) - .flatMap(e -> Optional.ofNullable(e.getPropertySetInfo()))); + .flatMap(e -> Optional.ofNullable(e.getPropertySetInfo()))); } public static Optional getPropertySetInfo(XPropertyContainer propertyContainer) { return Optional.ofNullable(propertyContainer).flatMap(UnoProperties::getPropertySetInfo); } - /* - * getPropertyNames - */ - public static List getPropertyNames(Property[] properties) { Objects.requireNonNull(properties); return (Arrays.stream(properties) - .map(p -> p.Name) - .collect(Collectors.toList())); + .map(p -> p.Name) + .collect(Collectors.toList())); } public static List getPropertyNames(XPropertySetInfo propertySetInfo) { @@ -67,13 +56,9 @@ public static List getPropertyNames(XPropertyContainer propertyContainer .orElse(new ArrayList<>())); } - /* - * getPropertyValue - */ - public static Optional getValueAsObject(XPropertySet propertySet, String property) - throws - WrappedTargetException { + throws + WrappedTargetException { Objects.requireNonNull(propertySet); Objects.requireNonNull(property); try { @@ -84,8 +69,8 @@ public static Optional getValueAsObject(XPropertySet propertySet, String } public static Optional getValueAsObject(XPropertyContainer propertyContainer, String property) - throws - WrappedTargetException { + throws + WrappedTargetException { Optional propertySet = asPropertySet(propertyContainer); if (propertySet.isEmpty()) { return Optional.empty(); diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoRedlines.java b/src/main/java/org/jabref/model/openoffice/uno/UnoRedlines.java index 2c408648d6f..3793ce7a079 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoRedlines.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoRedlines.java @@ -12,13 +12,13 @@ import com.sun.star.text.XTextDocument; /** - * Change tracking and Redlines + * Change tracking and Redlines */ public class UnoRedlines { public static boolean getRecordChanges(XTextDocument doc) - throws - WrappedTargetException { + throws + WrappedTargetException { // https://wiki.openoffice.org/wiki/Documentation/DevGuide/Text/Settings // "Properties of com.sun.star.text.TextDocument" diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoReferenceMark.java b/src/main/java/org/jabref/model/openoffice/uno/UnoReferenceMark.java index 7a719042d79..062564b8156 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoReferenceMark.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoReferenceMark.java @@ -17,17 +17,17 @@ public class UnoReferenceMark { - private UnoReferenceMark() { } + private UnoReferenceMark() { + } /** * @throws NoDocumentException If cannot get reference marks - * - * Note: also used by `isDocumentConnectionMissing` to test if we have a working connection. - * + *

+ * Note: also used by `isDocumentConnectionMissing` to test if we have a working connection. */ public static XNameAccess getNameAccess(XTextDocument doc) - throws - NoDocumentException { + throws + NoDocumentException { XReferenceMarksSupplier supplier = UnoCast.cast(XReferenceMarksSupplier.class, doc).get(); @@ -40,11 +40,11 @@ public static XNameAccess getNameAccess(XTextDocument doc) /** * Names of all reference marks. - * + *

* Empty list for nothing. */ public static List getListOfNames(XTextDocument doc) - throws NoDocumentException { + throws NoDocumentException { XNameAccess nameAccess = UnoReferenceMark.getNameAccess(doc); String[] names = nameAccess.getElementNames(); @@ -56,13 +56,13 @@ public static List getListOfNames(XTextDocument doc) /** * Remove the named reference mark. - * + *

* Removes both the text and the mark itself. */ public static void removeIfExists(XTextDocument doc, String name) - throws - WrappedTargetException, - NoDocumentException { + throws + WrappedTargetException, + NoDocumentException { XNameAccess xReferenceMarks = UnoReferenceMark.getNameAccess(doc); @@ -80,46 +80,41 @@ public static void removeIfExists(XTextDocument doc, String name) } /** - * @return reference mark as XTextContent, Optional.empty if not found. + * @return reference mark as XTextContent, Optional.empty if not found. */ public static Optional getAsTextContent(XTextDocument doc, String name) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { XNameAccess nameAccess = UnoReferenceMark.getNameAccess(doc); return UnoNameAccess.getTextContentByName(nameAccess, name); } /** - * XTextRange for the named reference mark, Optional.empty if not found. + * XTextRange for the named reference mark, Optional.empty if not found. */ public static Optional getAnchor(XTextDocument doc, String name) - throws - NoDocumentException, - WrappedTargetException { + throws + NoDocumentException, + WrappedTargetException { return (UnoReferenceMark.getAsTextContent(doc, name) - .map(XTextContent::getAnchor)); + .map(XTextContent::getAnchor)); } /** * Insert a new reference mark at the provided cursor position. + *

+ * If {@code absorb} is true, the text in the cursor range will become the text with gray background. + *

+ * Note: LibreOffice 6.4.6.2 will create multiple reference marks with the same name without error or renaming. Its GUI does not allow this, but we can create them programmatically. In the GUI, clicking on any of those identical names will move the cursor to the same mark. * - * If {@code absorb} is true, the text in the cursor range will become the text with gray - * background. - * - * Note: LibreOffice 6.4.6.2 will create multiple reference marks with the same name without - * error or renaming. - * Its GUI does not allow this, but we can create them programmatically. - * In the GUI, clicking on any of those identical names will move the cursor to the same - * mark. - * - * @param name For the reference mark. + * @param name For the reference mark. * @param range Cursor marking the location or range for the reference mark. */ public static XNamed create(XTextDocument doc, String name, XTextRange range, boolean absorb) - throws - CreationException { + throws + CreationException { return UnoNamed.insertNamedTextContent(doc, "com.sun.star.text.ReferenceMark", name, range, absorb); } } diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoScreenRefresh.java b/src/main/java/org/jabref/model/openoffice/uno/UnoScreenRefresh.java index e5c27fcbb5c..6de0afe5ad6 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoScreenRefresh.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoScreenRefresh.java @@ -7,17 +7,17 @@ */ public class UnoScreenRefresh { - private UnoScreenRefresh() { } + private UnoScreenRefresh() { + } /** * Disable screen refresh. - * + *

* Must be paired with unlockControllers() - * + *

* https://www.openoffice.org/api/docs/common/ref/com/sun/star/frame/XModel.html - * - * While there is at least one lock remaining, some - * notifications for display updates are not broadcasted. + *

+ * While there is at least one lock remaining, some notifications for display updates are not broadcasted. */ public static void lockControllers(XTextDocument doc) { doc.lockControllers(); diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoSelection.java b/src/main/java/org/jabref/model/openoffice/uno/UnoSelection.java index 43d89ed157b..a6e581cfce1 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoSelection.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoSelection.java @@ -17,7 +17,8 @@ public class UnoSelection { private static final Logger LOGGER = LoggerFactory.getLogger(UnoSelection.class); - private UnoSelection() { } + private UnoSelection() { + } private static Optional getSelectionSupplier(XTextDocument doc) { if (doc == null) { @@ -39,46 +40,21 @@ private static Optional getSelectionSupplier(XTextDocument d /** * @return may be Optional.empty(), or some type supporting XServiceInfo - * - * - * So far it seems the first thing we have to do - * with a selection is to decide what do we have. - * + * So far it seems the first thing we have to do with a selection is to decide what do we have. + *

* One way to do that is accessing its XServiceInfo interface. - * - * Experiments using printServiceInfo with cursor in various - * positions in the document: - * - * With cursor within the frame, in text: - * *** xserviceinfo.getImplementationName: "SwXTextRanges" - * "com.sun.star.text.TextRanges" - * - * With cursor somewhere else in text: - * *** xserviceinfo.getImplementationName: "SwXTextRanges" - * "com.sun.star.text.TextRanges" - * - * With cursor in comment (also known as "annotation"): - * *** XSelectionSupplier is OK - * *** Object initialSelection is null - * *** xserviceinfo is null - * - * With frame selected: - * *** xserviceinfo.getImplementationName: "SwXTextFrame" - * "com.sun.star.text.BaseFrame" - * "com.sun.star.text.TextContent" - * "com.sun.star.document.LinkTarget" - * "com.sun.star.text.TextFrame" - * "com.sun.star.text.Text" - * - * With cursor selecting an inserted image: - * *** XSelectionSupplier is OK - * *** Object initialSelection is OK - * *** xserviceinfo is OK - * *** xserviceinfo.getImplementationName: "SwXTextGraphicObject" - * "com.sun.star.text.BaseFrame" - * "com.sun.star.text.TextContent" - * "com.sun.star.document.LinkTarget" - * "com.sun.star.text.TextGraphicObject" + *

+ * Experiments using printServiceInfo with cursor in various positions in the document: + *

+ * With cursor within the frame, in text: *** xserviceinfo.getImplementationName: "SwXTextRanges" "com.sun.star.text.TextRanges" + *

+ * With cursor somewhere else in text: *** xserviceinfo.getImplementationName: "SwXTextRanges" "com.sun.star.text.TextRanges" + *

+ * With cursor in comment (also known as "annotation"): *** XSelectionSupplier is OK *** Object initialSelection is null *** xserviceinfo is null + *

+ * With frame selected: *** xserviceinfo.getImplementationName: "SwXTextFrame" "com.sun.star.text.BaseFrame" "com.sun.star.text.TextContent" "com.sun.star.document.LinkTarget" "com.sun.star.text.TextFrame" "com.sun.star.text.Text" + *

+ * With cursor selecting an inserted image: *** XSelectionSupplier is OK *** Object initialSelection is OK *** xserviceinfo is OK *** xserviceinfo.getImplementationName: "SwXTextGraphicObject" "com.sun.star.text.BaseFrame" "com.sun.star.text.TextContent" "com.sun.star.document.LinkTarget" "com.sun.star.text.TextGraphicObject" */ public static Optional getSelectionAsXServiceInfo(XTextDocument doc) { Objects.requireNonNull(doc); @@ -100,13 +76,9 @@ public static Optional getSelectionAsXServiceInfo(XTextDocument do } /** - * Select the object represented by {@code newSelection} if it is - * known and selectable in this {@code XSelectionSupplier} object. - * - * Presumably result from {@code XSelectionSupplier.getSelection()} is - * usually OK. It also accepted - * {@code XTextRange newSelection = doc.getText().getStart();} - * + * Select the object represented by {@code newSelection} if it is known and selectable in this {@code XSelectionSupplier} object. + *

+ * Presumably result from {@code XSelectionSupplier.getSelection()} is usually OK. It also accepted {@code XTextRange newSelection = doc.getText().getStart();} */ public static void select(XTextDocument doc, Object newSelection) { Objects.requireNonNull(doc); diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoStyle.java b/src/main/java/org/jabref/model/openoffice/uno/UnoStyle.java index cd4065ba8b3..aa659919fab 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoStyle.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoStyle.java @@ -18,11 +18,12 @@ public class UnoStyle { public static final String CHARACTER_STYLES = "CharacterStyles"; public static final String PARAGRAPH_STYLES = "ParagraphStyles"; - private UnoStyle() { } + private UnoStyle() { + } private static Optional getStyleFromFamily(XTextDocument doc, String familyName, String styleName) - throws - WrappedTargetException { + throws + WrappedTargetException { XStyleFamiliesSupplier fss = UnoCast.cast(XStyleFamiliesSupplier.class, doc).get(); XNameAccess families = UnoCast.cast(XNameAccess.class, fss.getStyleFamilies()).get(); @@ -43,34 +44,34 @@ private static Optional getStyleFromFamily(XTextDocument doc, String fam } public static Optional getParagraphStyle(XTextDocument doc, String styleName) - throws - WrappedTargetException { + throws + WrappedTargetException { return getStyleFromFamily(doc, PARAGRAPH_STYLES, styleName); } public static Optional getCharacterStyle(XTextDocument doc, String styleName) - throws - WrappedTargetException { + throws + WrappedTargetException { return getStyleFromFamily(doc, CHARACTER_STYLES, styleName); } public static Optional getInternalNameOfStyle(XTextDocument doc, String familyName, String name) - throws - WrappedTargetException { + throws + WrappedTargetException { return (getStyleFromFamily(doc, familyName, name) .map(XStyle::getName)); } public static Optional getInternalNameOfParagraphStyle(XTextDocument doc, String name) - throws - WrappedTargetException { + throws + WrappedTargetException { return getInternalNameOfStyle(doc, PARAGRAPH_STYLES, name); } public static Optional getInternalNameOfCharacterStyle(XTextDocument doc, String name) - throws - WrappedTargetException { + throws + WrappedTargetException { return getInternalNameOfStyle(doc, CHARACTER_STYLES, name); } } diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoTextDocument.java b/src/main/java/org/jabref/model/openoffice/uno/UnoTextDocument.java index c7e94e6bb4c..3f154a4ae9c 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoTextDocument.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoTextDocument.java @@ -7,6 +7,7 @@ import com.sun.star.document.XDocumentPropertiesSupplier; import com.sun.star.frame.XController; import com.sun.star.frame.XFrame; +import com.sun.star.lang.DisposedException; import com.sun.star.lang.WrappedTargetException; import com.sun.star.text.XTextDocument; import org.slf4j.Logger; @@ -16,25 +17,21 @@ public class UnoTextDocument { private static final Logger LOGGER = LoggerFactory.getLogger(UnoTextDocument.class); - private UnoTextDocument() { } + private UnoTextDocument() { + } /** - * @return True if we cannot reach the current document. + * @return True if we cannot reach the current document. */ public static boolean isDocumentConnectionMissing(XTextDocument doc) { - boolean missing = false; - if (doc == null) { - missing = true; - } + boolean missing = doc == null; // Attempt to check document is really available if (!missing) { try { UnoReferenceMark.getNameAccess(doc); - } catch (NoDocumentException ex) { - missing = true; - } catch (com.sun.star.lang.DisposedException ex) { + } catch (NoDocumentException | DisposedException ex) { missing = true; } } @@ -54,8 +51,8 @@ public static Optional getCurrentController(XTextDocument doc) { } /** - * @param doc The XTextDocument we want the frame title for. Null allowed. - * @return The title or Optional.empty() + * @param doc The XTextDocument we want the frame title for. Null allowed. + * @return The title or Optional.empty() */ public static Optional getFrameTitle(XTextDocument doc) { @@ -84,8 +81,8 @@ public static Optional getFrameTitle(XTextDocument doc) { static Optional getDocumentProperties(XTextDocument doc) { return (Optional.ofNullable(doc) - .flatMap(e -> UnoCast.cast(XDocumentPropertiesSupplier.class, e)) - .map(XDocumentPropertiesSupplier::getDocumentProperties)); + .flatMap(e -> UnoCast.cast(XDocumentPropertiesSupplier.class, e)) + .map(XDocumentPropertiesSupplier::getDocumentProperties)); } } diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoTextRange.java b/src/main/java/org/jabref/model/openoffice/uno/UnoTextRange.java index 56cb1498e2a..520878833e5 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoTextRange.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoTextRange.java @@ -3,33 +3,30 @@ import java.util.Optional; import com.sun.star.text.XFootnote; +import com.sun.star.text.XTextContent; import com.sun.star.text.XTextRange; import com.sun.star.text.XTextRangeCompare; public class UnoTextRange { - private UnoTextRange() { } + private UnoTextRange() { + } /** - * If original is in a footnote, return a range containing - * the corresponding footnote marker. - * - * Returns Optional.empty if not in a footnote. + * If original is in a footnote, return a range containing the corresponding footnote marker. + *

+ * Returns Optional.empty if not in a footnote. */ public static Optional getFootnoteMarkRange(XTextRange original) { Optional footer = UnoCast.cast(XFootnote.class, original.getText()); - if (footer.isPresent()) { - // If we are inside a footnote, - // find the linking footnote marker: - // The footnote's anchor gives the correct position in the text: - return Optional.ofNullable(footer.get().getAnchor()); - } - return Optional.empty(); + // If we are inside a footnote, + // find the linking footnote marker: + // The footnote's anchor gives the correct position in the text: + return footer.map(XTextContent::getAnchor); } /** - * Test if two XTextRange values are comparable (i.e. they share - * the same getText()). + * Test if two XTextRange values are comparable (i.e. they share the same getText()). */ public static boolean comparables(XTextRange a, XTextRange b) { return a.getText() == b.getText(); @@ -37,7 +34,6 @@ public static boolean comparables(XTextRange a, XTextRange b) { /** * @return follows java conventions - * * 1 if (a > b); (-1) if (a < b) */ public static int compareStartsUnsafe(XTextRangeCompare compare, XTextRange a, XTextRange b) { @@ -54,7 +50,6 @@ public static int compareStarts(XTextRange a, XTextRange b) { /** * @return follows java conventions - * * 1 if (a > b); (-1) if (a < b) */ public static int compareEnds(XTextRange a, XTextRange b) { diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoTextSection.java b/src/main/java/org/jabref/model/openoffice/uno/UnoTextSection.java index 3b9797f553e..56293eee947 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoTextSection.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoTextSection.java @@ -17,11 +17,11 @@ public class UnoTextSection { /** - * @return An XNameAccess to find sections by name. + * @return An XNameAccess to find sections by name. */ public static XNameAccess getNameAccess(XTextDocument doc) - throws - NoDocumentException { + throws + NoDocumentException { XTextSectionsSupplier supplier = UnoCast.cast(XTextSectionsSupplier.class, doc).get(); try { @@ -32,12 +32,12 @@ public static XNameAccess getNameAccess(XTextDocument doc) } /** - * Get an XTextSection by name. + * Get an XTextSection by name. */ public static Optional getByName(XTextDocument doc, String name) - throws - WrappedTargetException, - NoDocumentException { + throws + WrappedTargetException, + NoDocumentException { XNameAccess nameAccess = getNameAccess(doc); try { return Optional.ofNullable((XTextSection) ((Any) nameAccess.getByName(name)).getObject()); @@ -53,26 +53,25 @@ public static Optional getByName(XTextDocument doc, String name) * @return The XTextRange for the section, or Optional.empty(). */ public static Optional getAnchor(XTextDocument doc, String name) - throws - WrappedTargetException, - NoDocumentException { + throws + WrappedTargetException, + NoDocumentException { XNameAccess nameAccess = getNameAccess(doc); return (UnoNameAccess.getTextContentByName(nameAccess, name).map(XTextContent::getAnchor)); } /** - * Create a text section with the provided name and insert it at the provided cursor. + * Create a text section with the provided name and insert it at the provided cursor. * - * @param name The desired name for the section. - * @param range The location to insert at. - * - * If an XTextSection by that name already exists, LibreOffice (6.4.6.2) creates a section with - * a name different from what we requested, in "Section {number}" format. + * @param name The desired name for the section. + * @param range The location to insert at. + *

+ * If an XTextSection by that name already exists, LibreOffice (6.4.6.2) creates a section with a name different from what we requested, in "Section {number}" format. */ public static XNamed create(XTextDocument doc, String name, XTextRange range, boolean absorb) - throws - CreationException { + throws + CreationException { return UnoNamed.insertNamedTextContent(doc, "com.sun.star.text.TextSection", name, range, absorb); } diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoUndo.java b/src/main/java/org/jabref/model/openoffice/uno/UnoUndo.java index 4f08bb0ed5c..bd1f85e1690 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoUndo.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoUndo.java @@ -12,23 +12,20 @@ */ public class UnoUndo { - private UnoUndo() { } + private UnoUndo() { + } public static Optional getXUndoManager(XTextDocument doc) { // https://www.openoffice.org/api/docs/common/ref/com/sun/star/document/XUndoManager.html return (UnoCast.cast(XUndoManagerSupplier.class, doc) - .map(XUndoManagerSupplier::getUndoManager)); + .map(XUndoManagerSupplier::getUndoManager)); } /** - * Each call to enterUndoContext must be paired by a call to leaveUndoContext, otherwise, the - * document's undo stack is left in an inconsistent state. + * Each call to enterUndoContext must be paired by a call to leaveUndoContext, otherwise, the document's undo stack is left in an inconsistent state. */ public static void enterUndoContext(XTextDocument doc, String title) { - Optional undoManager = getXUndoManager(doc); - if (undoManager.isPresent()) { - undoManager.get().enterUndoContext(title); - } + getXUndoManager(doc).ifPresent(undoManager -> undoManager.enterUndoContext(title)); } public static void leaveUndoContext(XTextDocument doc) { diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoUserDefinedProperty.java b/src/main/java/org/jabref/model/openoffice/uno/UnoUserDefinedProperty.java index f5ec43f3211..85158db2d1c 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoUserDefinedProperty.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoUserDefinedProperty.java @@ -24,14 +24,15 @@ /** * Document level user-defined properties. - * - * LibreOffice GUI: [File]/[Properties]/[Custom Properties] + *

+ * LibreOffice GUI: [File]/[Properties]/[Custom Properties] */ public class UnoUserDefinedProperty { private static final Logger LOGGER = LoggerFactory.getLogger(UnoUserDefinedProperty.class); - private UnoUserDefinedProperty() { } + private UnoUserDefinedProperty() { + } public static Optional getPropertyContainer(XTextDocument doc) { return UnoTextDocument.getDocumentProperties(doc).map(XDocumentProperties::getUserDefinedProperties); @@ -39,24 +40,20 @@ public static Optional getPropertyContainer(XTextDocument do public static List getListOfNames(XTextDocument doc) { return (UnoUserDefinedProperty.getPropertyContainer(doc) - .map(UnoProperties::getPropertyNames) - .orElse(new ArrayList<>())); + .map(UnoProperties::getPropertyNames) + .orElse(new ArrayList<>())); } /** * @param property Name of a custom document property in the current document. - * * @return The value of the property or Optional.empty() - * - * These properties are used to store extra data about individual citation. - * In particular, the `pageInfo` part. - * + * These properties are used to store extra data about individual citation. In particular, the `pageInfo` part. */ public static Optional getStringValue(XTextDocument doc, String property) - throws - WrappedTargetException { + throws + WrappedTargetException { Optional propertySet = (UnoUserDefinedProperty.getPropertyContainer(doc) - .flatMap(UnoProperties::asPropertySet)); + .flatMap(UnoProperties::asPropertySet)); if (propertySet.isEmpty()) { throw new java.lang.IllegalArgumentException("getting UserDefinedProperties as XPropertySet failed"); } @@ -69,16 +66,14 @@ public static Optional getStringValue(XTextDocument doc, String property } /** - * @param property Name of a custom document property in the current document. - * Created if does not exist yet. - * - * @param value The value to be stored. + * @param property Name of a custom document property in the current document. Created if does not exist yet. + * @param value The value to be stored. */ public static void setStringProperty(XTextDocument doc, String property, String value) - throws - IllegalTypeException, - PropertyVetoException, - WrappedTargetException { + throws + IllegalTypeException, + PropertyVetoException, + WrappedTargetException { Objects.requireNonNull(property); Objects.requireNonNull(value); @@ -114,12 +109,12 @@ public static void setStringProperty(XTextDocument doc, String property, String /** * @param property Name of a custom document property in the current document. - * - * Logs warning if does not exist. + *

+ * Logs warning if does not exist. */ public static void remove(XTextDocument doc, String property) - throws - NotRemoveableException { + throws + NotRemoveableException { Objects.requireNonNull(property); @@ -133,18 +128,18 @@ public static void remove(XTextDocument doc, String property) container.get().removeProperty(property); } catch (UnknownPropertyException ex) { LOGGER.warn(String.format("UnoUserDefinedProperty.remove(%s) This property was not there to remove", - property)); + property)); } } /** * @param property Name of a custom document property in the current document. - * - * Keep silent if property did not exist. + *

+ * Keep silent if property did not exist. */ public static void removeIfExists(XTextDocument doc, String property) - throws - NotRemoveableException { + throws + NotRemoveableException { Objects.requireNonNull(property); diff --git a/src/main/java/org/jabref/model/openoffice/util/OOListUtil.java b/src/main/java/org/jabref/model/openoffice/util/OOListUtil.java index 59295c798fe..c61b81655a7 100644 --- a/src/main/java/org/jabref/model/openoffice/util/OOListUtil.java +++ b/src/main/java/org/jabref/model/openoffice/util/OOListUtil.java @@ -1,6 +1,5 @@ package org.jabref.model.openoffice.util; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.function.Function; @@ -10,22 +9,22 @@ public class OOListUtil { public static List map(List list, Function fun) { - return list.stream().map(e -> fun.apply(e)).collect(Collectors.toList()); + return list.stream().map(fun).collect(Collectors.toList()); } - /** Integers 0..(len-1) */ + /** + * Integers 0..(len-1) + */ public static List makeIndices(int len) { return Stream.iterate(0, i -> i + 1).limit(len).collect(Collectors.toList()); } - /** Return indices so that list.get(indices.get(i)) is sorted. */ + /** + * Return indices so that list.get(indices.get(i)) is sorted. + */ public static List order(List list, Comparator comparator) { List indices = makeIndices(list.size()); - Collections.sort(indices, new Comparator() { - @Override public int compare(final Integer a, final Integer b) { - return comparator.compare((U) list.get(a), (U) list.get(b)); - } - }); + indices.sort((a, b) -> comparator.compare(list.get(a), list.get(b))); return indices; } } diff --git a/src/main/java/org/jabref/model/openoffice/util/OOPair.java b/src/main/java/org/jabref/model/openoffice/util/OOPair.java index 9d1596d19ef..006c33e942e 100644 --- a/src/main/java/org/jabref/model/openoffice/util/OOPair.java +++ b/src/main/java/org/jabref/model/openoffice/util/OOPair.java @@ -3,6 +3,7 @@ public class OOPair { public final A a; public final B b; + public OOPair(A a, B b) { this.a = a; this.b = b; diff --git a/src/main/java/org/jabref/model/openoffice/util/OOResult.java b/src/main/java/org/jabref/model/openoffice/util/OOResult.java index c90bc68ad13..afbdeaf617e 100644 --- a/src/main/java/org/jabref/model/openoffice/util/OOResult.java +++ b/src/main/java/org/jabref/model/openoffice/util/OOResult.java @@ -91,12 +91,16 @@ public OOResult mapError(Function fun) { } } - /** Throw away the error part. */ + /** + * Throw away the error part. + */ public Optional getOptional() { return result; } - /** Throw away the result part. */ + /** + * Throw away the result part. + */ public OOVoidResult asVoidResult() { if (isError()) { return OOVoidResult.error(getError()); @@ -104,6 +108,5 @@ public OOVoidResult asVoidResult() { return OOVoidResult.ok(); } } - } diff --git a/src/main/java/org/jabref/model/openoffice/util/OOTuple3.java b/src/main/java/org/jabref/model/openoffice/util/OOTuple3.java index 8742e06764f..cff3079efa1 100644 --- a/src/main/java/org/jabref/model/openoffice/util/OOTuple3.java +++ b/src/main/java/org/jabref/model/openoffice/util/OOTuple3.java @@ -1,13 +1,11 @@ package org.jabref.model.openoffice.util; /** - * This class allows three objects to be packed together, and later accessed as fields `a`, `b` and - * `c`. - * + * This class allows three objects to be packed together, and later accessed as fields `a`, `b` and `c`. + *

* Can be used to avoid creating a new class for just this purpose. - * + *

* Can be useful if you do not have `Trifunction` at hand but need to pass three objects at a time. - * */ public class OOTuple3 { diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 0a734625bb5..6b6d7dff88a 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -542,6 +542,10 @@ Moved\ group\ "%0".=Moved group "%0". Mr.\ DLib\ Privacy\ settings=Mr. DLib Privacy settings +No\ database\ is\ open=No database is open + +We\ need\ a\ database\ to\ export\ from.\ Open\ one.=We need a database to export from. Open one. + No\ recommendations\ received\ from\ Mr.\ DLib\ for\ this\ entry.=No recommendations received from Mr. DLib for this entry. Error\ while\ fetching\ recommendations\ from\ Mr.DLib.=Error while fetching recommendations from Mr.DLib. @@ -1043,9 +1047,9 @@ Cite\ special=Cite special Extra\ information\ (e.g.\ page\ number)=Extra information (e.g. page number) Manage\ citations=Manage citations Problem\ modifying\ citation=Problem modifying citation +Problem\ collecting\ citations=Problem collecting citations Citation=Citation Connecting...=Connecting... -Could\ not\ resolve\ BibTeX\ entry\ for\ citation\ marker\ '%0'.=Could not resolve BibTeX entry for citation marker '%0'. Select\ style=Select style Journals=Journals Cite=Cite @@ -1057,17 +1061,36 @@ Select\ Writer\ document=Select Writer document Sync\ OpenOffice/LibreOffice\ bibliography=Sync OpenOffice/LibreOffice bibliography Select\ which\ open\ Writer\ document\ to\ work\ on=Select which open Writer document to work on Connected\ to\ document=Connected to document + +Could\ not\ connect\ to\ any\ Writer\ document.\ Please\ make\ sure\ a\ document\ is\ open\ before\ using\ the\ 'Select\ Writer\ document'\ button\ to\ connect\ to\ it.=Could not connect to any Writer document. Please make sure a document is open before using the 'Select Writer document' button to connect to it. + +No\ Writer\ documents\ found=No Writer documents found + Insert\ a\ citation\ without\ text\ (the\ entry\ will\ appear\ in\ the\ reference\ list)=Insert a citation without text (the entry will appear in the reference list) Cite\ selected\ entries\ with\ extra\ information=Cite selected entries with extra information Ensure\ that\ the\ bibliography\ is\ up-to-date=Ensure that the bibliography is up-to-date + Your\ OpenOffice/LibreOffice\ document\ references\ the\ citation\ key\ '%0',\ which\ could\ not\ be\ found\ in\ your\ current\ library.=Your OpenOffice/LibreOffice document references the citation key '%0', which could not be found in your current library. + +This\ operation\ requires\ a\ bibliography\ database.=This operation requires a bibliography database. + +Your\ OpenOffice/LibreOffice\ document\ references\ at\ least\ %0\ citation\ keys\ which\ could\ not\ be\ found\ in\ your\ current\ library.\ Some\ of\ these\ are\ %1.=Your OpenOffice/LibreOffice document references at least %0 citation keys which could not be found in your current library. Some of these are %1. + +Your\ OpenOffice/LibreOffice\ document\ references\ no\ citation\ keys\ which\ could\ also\ be\ found\ in\ your\ current\ library.=Your OpenOffice/LibreOffice document references no citation keys which could also be found in your current library. + Unable\ to\ synchronize\ bibliography=Unable to synchronize bibliography Combine\ pairs\ of\ citations\ that\ are\ separated\ by\ spaces\ only=Combine pairs of citations that are separated by spaces only Autodetection\ failed=Autodetection failed Please\ wait...=Please wait... Connection\ lost=Connection lost -The\ paragraph\ format\ is\ controlled\ by\ the\ property\ 'ReferenceParagraphFormat'\ or\ 'ReferenceHeaderParagraphFormat'\ in\ the\ style\ file.=The paragraph format is controlled by the property 'ReferenceParagraphFormat' or 'ReferenceHeaderParagraphFormat' in the style file. -The\ character\ format\ is\ controlled\ by\ the\ citation\ property\ 'CitationCharacterFormat'\ in\ the\ style\ file.=The character format is controlled by the citation property 'CitationCharacterFormat' in the style file. + +Could\ not\ update\ bibliography=Could not update bibliography +Not\ connected\ to\ document=Not connected to document +Problem\ combining\ cite\ markers=Problem combining cite markers +Unable\ to\ reload\ style\ file=Unable to reload style file + +Problem\ during\ separating\ cite\ markers=Problem during separating cite markers + Automatically\ sync\ bibliography\ when\ inserting\ citations=Automatically sync bibliography when inserting citations Look\ up\ BibTeX\ entries\ in\ the\ active\ tab\ only=Look up BibTeX entries in the active tab only Look\ up\ BibTeX\ entries\ in\ all\ open\ libraries=Look up BibTeX entries in all open libraries @@ -1169,7 +1192,9 @@ Toggle\ relevance=Toggle relevance Toggle\ quality\ assured=Toggle quality assured Toggle\ print\ status=Toggle print status Update\ keywords=Update keywords +Problem\ connecting=Problem connecting Connection\ to\ OpenOffice/LibreOffice\ has\ been\ lost.\ Please\ make\ sure\ OpenOffice/LibreOffice\ is\ running,\ and\ try\ to\ reconnect.=Connection to OpenOffice/LibreOffice has been lost. Please make sure OpenOffice/LibreOffice is running, and try to reconnect. + JabRef\ will\ send\ at\ least\ one\ request\ per\ entry\ to\ a\ publisher.=JabRef will send at least one request per entry to a publisher. Correct\ the\ entry,\ and\ reopen\ editor\ to\ display/edit\ source.=Correct the entry, and reopen editor to display/edit source. Could\ not\ connect\ to\ running\ OpenOffice/LibreOffice.=Could not connect to running OpenOffice/LibreOffice. @@ -1183,8 +1208,6 @@ To\ disable\ the\ memory\ stick\ mode\ rename\ or\ remove\ the\ jabref.xml\ file Unable\ to\ connect.\ One\ possible\ reason\ is\ that\ JabRef\ and\ OpenOffice/LibreOffice\ are\ not\ both\ running\ in\ either\ 32\ bit\ mode\ or\ 64\ bit\ mode.=Unable to connect. One possible reason is that JabRef and OpenOffice/LibreOffice are not both running in either 32 bit mode or 64 bit mode. Delimiter(s)=Delimiter(s) When\ downloading\ files,\ or\ moving\ linked\ files\ to\ the\ file\ directory,\ use\ the\ bib\ file\ location.=When downloading files, or moving linked files to the file directory, use the bib file location. -Your\ style\ file\ specifies\ the\ character\ format\ '%0',\ which\ is\ undefined\ in\ your\ current\ OpenOffice/LibreOffice\ document.=Your style file specifies the character format '%0', which is undefined in your current OpenOffice/LibreOffice document. -Your\ style\ file\ specifies\ the\ paragraph\ format\ '%0',\ which\ is\ undefined\ in\ your\ current\ OpenOffice/LibreOffice\ document.=Your style file specifies the paragraph format '%0', which is undefined in your current OpenOffice/LibreOffice document. Searching...=Searching... Please\ enter\ a\ search\ string=Please enter a search string @@ -1564,6 +1587,22 @@ Custom=Custom Export\ cited=Export cited Unable\ to\ generate\ new\ library=Unable to generate new library +The\ cursor\ is\ in\ a\ protected\ area.=The cursor is in a protected area. +JabRefException=JabRefException +No\ bibliography\ database\ is\ open\ for\ citation.=No bibliography database is open for citation. + +No\ database\ is\ open\ for\ updating\ citation\ markers\ after\ citing.=No database is open for updating citation markers after citing. + +No\ bibliography\ entries\ are\ selected\ for\ citation.=No bibliography entries are selected for citation. +No\ bibliography\ style\ is\ selected\ for\ citation.=No bibliography style is selected for citation. +No\ database=No database + +No\ entries\ selected\ for\ citation=No entries selected for citation +Open\ one\ before\ citing.=Open one before citing. + +Select\ one\ before\ citing.=Select one before citing. +Select\ some\ before\ citing.=Select some before citing. + Found\ identical\ ranges=Found identical ranges Found\ overlapping\ ranges=Found overlapping ranges Found\ touching\ ranges=Found touching ranges @@ -1832,9 +1871,6 @@ Could\ not\ connect\ to\ Vim\ server.\ Make\ sure\ that\ Vim\ is\ running\ with\ Could\ not\ connect\ to\ a\ running\ gnuserv\ process.\ Make\ sure\ that\ Emacs\ or\ XEmacs\ is\ running,\ and\ that\ the\ server\ has\ been\ started\ (by\ running\ the\ command\ 'server-start'/'gnuserv-start').=Could not connect to a running gnuserv process. Make sure that Emacs or XEmacs is running, and that the server has been started (by running the command 'server-start'/'gnuserv-start'). Error\ pushing\ entries=Error pushing entries -Undefined\ character\ format=Undefined character format -Undefined\ paragraph\ format=Undefined paragraph format - Preamble=Preamble Markings=Markings Use\ selected\ instance=Use selected instance @@ -2314,8 +2350,6 @@ Separate\ merged\ citations=Separate merged citations Separate\ citations=Separate citations Unprotect\ terms=Unprotect terms -Error\ connecting\ to\ Writer\ document=Error connecting to Writer document -You\ need\ to\ open\ Writer\ with\ a\ document\ before\ connecting=You need to open Writer with a document before connecting Generate\ a\ new\ key\ for\ imported\ entries\ (overwriting\ their\ default)=Generate a new key for imported entries (overwriting their default) Warn\ about\ duplicates\ on\ import=Warn about duplicates on import @@ -2324,6 +2358,35 @@ Custom\ DOI\ URI=Custom DOI URI Use\ custom\ DOI\ base\ URI\ for\ article\ access=Use custom DOI base URI for article access Cited\ on\ pages=Cited on pages +Please\ move\ the\ cursor\ into\ the\ document\ text.=Please\ move\ the\ cursor\ into\ the\ document\ text. +To\ get\ the\ visual\ positions\ of\ your\ citations\ I\ need\ to\ move\ the\ cursor\ around,\ but\ could\ not\ get\ it.=To\ get\ the\ visual\ positions\ of\ your\ citations\ I\ need\ to\ move\ the\ cursor\ around,\ but\ could\ not\ get\ it. + +I\ cannot\ insert\ to\ the\ cursors\ current\ location.=I\ cannot\ insert\ to\ the\ cursors\ current\ location. + +Please\ move\ the\ cursor\ to\ the\ location\ for\ the\ new\ citation.=Please\ move\ the\ cursor\ to\ the\ location\ for\ the\ new\ citation. + +Please\ create\ it\ in\ the\ document\ or\ change\ in\ the\ file\:=Please create it in the document or change in the file: + +Please\ use\ the\ latter\ in\ the\ style\ file\ below\ to\ avoid\ localization\ problems.=Please use the latter in the style file below to avoid localization problems. + +The\ %0\ character\ style\ '%1'\ is\ a\ display\ name\ for\ '%2'.=The %0 character style '%1' is a display name for '%2'. + +The\ %0\ character\ style\ '%1'\ is\ missing\ from\ the\ document=The %0 character style '%1' is missing from the document + +The\ %0\ paragraph\ style\ '%1'\ is\ a\ display\ name\ for\ '%2'.=The %0 paragraph style '%1' is a display name for '%2'. + +The\ %0\ paragraph\ style\ '%1'\ is\ missing\ from\ the\ document=The %0 paragraph style '%1' is missing from the document + +Error\ while\ checking\ if\ Writer\ is\ recording\ changes\ or\ has\ recorded\ changes.=Error while checking if Writer is recording changes or has recorded changes. + +Cannot\ work\ with\ [Edit]/[Track\ Changes]/[Record]\ turned\ on.=Cannot work with [Edit]/[Track Changes]/[Record] turned on. + +Changes\ by\ JabRef\ could\ result\ in\ unexpected\ interactions\ with\ recorded\ changes.=Changes by JabRef could result in unexpected interactions with recorded changes. + +Recording\ and/or\ Recorded\ changes=Recording and/or Recorded changes + +Use\ [Edit]/[Track\ Changes]/[Manage]\ to\ resolve\ them\ first.=Use [Edit]/[Track Changes]/[Manage] to resolve them first. + Unable\ to\ find\ valid\ certification\ path\ to\ requested\ target(%0),\ download\ anyway?=Unable to find valid certification path to requested target(%0), download anyway? Download\ operation\ canceled.=Download operation canceled. diff --git a/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTest.java b/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTest.java index cd72bf9e858..a76c9ae91b3 100644 --- a/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTest.java +++ b/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTest.java @@ -1,11 +1,9 @@ package org.jabref.logic.openoffice.style; -import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -61,8 +59,8 @@ void testAuthorYear() throws IOException { @Test void testAuthorYearAsFile() throws URISyntaxException, IOException { - File defFile = Path.of(OOBibStyleTest.class.getResource(StyleLoader.DEFAULT_AUTHORYEAR_STYLE_PATH).toURI()) - .toFile(); + Path defFile = Path.of(OOBibStyleTest.class.getResource(StyleLoader.DEFAULT_AUTHORYEAR_STYLE_PATH).toURI()); + OOBibStyle style = new OOBibStyle(defFile, layoutFormatterPreferences); assertTrue(style.isValid()); assertFalse(style.isInternalStyle()); @@ -117,10 +115,10 @@ static CitationMarkerEntry makeCitationMarkerEntry(BibEntry entry, String pageInfoQ, boolean isFirstAppearanceOfSource) { return OOBibStyleTestHelper.makeCitationMarkerEntry(entry, - database, - uniqueLetterQ, - pageInfoQ, - isFirstAppearanceOfSource); + database, + uniqueLetterQ, + pageInfoQ, + isFirstAppearanceOfSource); } /* @@ -135,12 +133,28 @@ static String getCitationMarker2(OOBibStyle style, Boolean[] isFirstAppearanceOfSource, String[] pageInfo) { return OOBibStyleTestHelper.getCitationMarker2(style, - entries, - entryDBMap, - inParenthesis, - uniquefiers, - isFirstAppearanceOfSource, - pageInfo); + entries, + entryDBMap, + inParenthesis, + uniquefiers, + isFirstAppearanceOfSource, + pageInfo); + } + + static String getCitationMarker2b(OOBibStyle style, + List entries, + Map entryDBMap, + boolean inParenthesis, + String[] uniquefiers, + Boolean[] isFirstAppearanceOfSource, + String[] pageInfo) { + return OOBibStyleTestHelper.getCitationMarker2b(style, + entries, + entryDBMap, + inParenthesis, + uniquefiers, + isFirstAppearanceOfSource, + pageInfo); } /* @@ -151,35 +165,16 @@ static String getCitationMarker2(OOBibStyle style, void testGetNumCitationMarker() throws IOException { OOBibStyle style = new OOBibStyle(StyleLoader.DEFAULT_NUMERICAL_STYLE_PATH, layoutFormatterPreferences); - assertEquals("[1] ", style.getNumCitationMarker(Arrays.asList(1), -1, true)); - assertEquals("[1] ", runGetNumCitationMarker2a(style, Arrays.asList(1), -1, true)); + assertEquals("[1] ", runGetNumCitationMarker2a(style, List.of(1), -1, true)); - assertEquals("[1]", style.getNumCitationMarker(Arrays.asList(1), -1, false)); - assertEquals("[1]", runGetNumCitationMarker2a(style, Arrays.asList(1), -1, false)); + assertEquals("[1]", runGetNumCitationMarker2a(style, List.of(1), -1, false)); assertEquals("[1]", runGetNumCitationMarker2b(style, -1, numEntry("key", 1, null))); - assertEquals("[1] ", style.getNumCitationMarker(Arrays.asList(1), 0, true)); - assertEquals("[1] ", runGetNumCitationMarker2a(style, Arrays.asList(1), 0, true)); - - /* - * The following tests as for a numeric label for a - * bibliography entry containing more than one numbers. - * We do not need this, not reproduced. - */ - assertEquals("[1-3] ", style.getNumCitationMarker(Arrays.asList(1, 2, 3), 1, true)); - assertEquals("[1; 2; 3] ", style.getNumCitationMarker(Arrays.asList(1, 2, 3), 5, true)); - assertEquals("[1; 2; 3] ", style.getNumCitationMarker(Arrays.asList(1, 2, 3), -1, true)); - assertEquals("[1; 3; 12] ", style.getNumCitationMarker(Arrays.asList(1, 12, 3), 1, true)); - assertEquals("[3-5; 7; 10-12] ", style.getNumCitationMarker(Arrays.asList(12, 7, 3, 4, 11, 10, 5), 1, true)); - - String citation = style.getNumCitationMarker(Arrays.asList(1), -1, false); - assertEquals("[1; pp. 55-56]", style.insertPageInfo(citation, "pp. 55-56")); + assertEquals("[1] ", runGetNumCitationMarker2a(style, List.of(1), 0, true)); CitationMarkerNumericEntry e2 = numEntry("key", 1, "pp. 55-56"); - assertEquals(true, e2.getPageInfo().isPresent()); + assertTrue(e2.getPageInfo().isPresent()); assertEquals("pp. 55-56", e2.getPageInfo().get().toString()); - citation = runGetNumCitationMarker2b(style, -1, e2); - assertEquals("[1; pp. 55-56]", citation); OOBibStyleTestHelper.testGetNumCitationMarkerExtra(style); } @@ -188,68 +183,48 @@ void testGetNumCitationMarker() throws IOException { void testGetNumCitationMarkerUndefined() throws IOException { OOBibStyle style = new OOBibStyle(StyleLoader.DEFAULT_NUMERICAL_STYLE_PATH, layoutFormatterPreferences); - /* - * Testing bibliography labels with multiple numbers again. - * Not reproduced. - */ - assertEquals("[" + OOBibStyle.UNDEFINED_CITATION_MARKER + "; 2-4] ", - style.getNumCitationMarker(Arrays.asList(4, 2, 3, 0), 1, true)); - - assertEquals("[" + OOBibStyle.UNDEFINED_CITATION_MARKER + "] ", - style.getNumCitationMarker(Arrays.asList(0), 1, true)); - - assertEquals("[" + OOBibStyle.UNDEFINED_CITATION_MARKER + "; 1-3] ", - style.getNumCitationMarker(Arrays.asList(1, 2, 3, 0), 1, true)); - - assertEquals("[" + OOBibStyle.UNDEFINED_CITATION_MARKER + "; " + OOBibStyle.UNDEFINED_CITATION_MARKER + "; " - + OOBibStyle.UNDEFINED_CITATION_MARKER + "] ", - style.getNumCitationMarker(Arrays.asList(0, 0, 0), 1, true)); - - /* - * We have these instead: - */ // unresolved citations look like [??key] assertEquals("[" + OOBibStyle.UNDEFINED_CITATION_MARKER + "key" + "]", - runGetNumCitationMarker2b(style, 1, - numEntry("key", 0, null))); + runGetNumCitationMarker2b(style, 1, + numEntry("key", 0, null))); // pageInfo is shown for unresolved citations assertEquals("[" + OOBibStyle.UNDEFINED_CITATION_MARKER + "key" + "; p1]", - runGetNumCitationMarker2b(style, 1, - numEntry("key", 0, "p1"))); + runGetNumCitationMarker2b(style, 1, + numEntry("key", 0, "p1"))); // unresolved citations sorted to the front assertEquals("[" + OOBibStyle.UNDEFINED_CITATION_MARKER + "key" + "; 2-4]", - runGetNumCitationMarker2b(style, 1, - numEntry("x4", 4, ""), - numEntry("x2", 2, ""), - numEntry("x3", 3, ""), - numEntry("key", 0, ""))); + runGetNumCitationMarker2b(style, 1, + numEntry("x4", 4, ""), + numEntry("x2", 2, ""), + numEntry("x3", 3, ""), + numEntry("key", 0, ""))); assertEquals("[" + OOBibStyle.UNDEFINED_CITATION_MARKER + "key" + "; 1-3]", - runGetNumCitationMarker2b(style, 1, - numEntry("x1", 1, ""), - numEntry("x2", 2, ""), - numEntry("y3", 3, ""), - numEntry("key", 0, ""))); + runGetNumCitationMarker2b(style, 1, + numEntry("x1", 1, ""), + numEntry("x2", 2, ""), + numEntry("y3", 3, ""), + numEntry("key", 0, ""))); // multiple unresolved citations are not collapsed assertEquals("[" - + OOBibStyle.UNDEFINED_CITATION_MARKER + "x1" + "; " - + OOBibStyle.UNDEFINED_CITATION_MARKER + "x2" + "; " - + OOBibStyle.UNDEFINED_CITATION_MARKER + "x3" + "]", - runGetNumCitationMarker2b(style, 1, - numEntry("x1", 0, ""), - numEntry("x2", 0, ""), - numEntry("x3", 0, ""))); + + OOBibStyle.UNDEFINED_CITATION_MARKER + "x1" + "; " + + OOBibStyle.UNDEFINED_CITATION_MARKER + "x2" + "; " + + OOBibStyle.UNDEFINED_CITATION_MARKER + "x3" + "]", + runGetNumCitationMarker2b(style, 1, + numEntry("x1", 0, ""), + numEntry("x2", 0, ""), + numEntry("x3", 0, ""))); /* * BIBLIOGRAPHY */ CitationMarkerNumericBibEntry x = numBibEntry("key", Optional.empty()); assertEquals("[" + OOBibStyle.UNDEFINED_CITATION_MARKER + "key" + "] ", - style.getNumCitationMarkerForBibliography(x).toString()); + style.getNumCitationMarkerForBibliography(x).toString()); } @Test @@ -292,28 +267,22 @@ void testGetCitationMarker() throws IOException { assertEquals(-1, style.getMaxAuthorsFirst()); assertEquals("[Boström et al., 2006]", - style.getCitationMarker(Collections.singletonList(entry), entryDBMap, true, null, null)); - assertEquals("[Boström et al., 2006]", - getCitationMarker2(style, - Collections.singletonList(entry), entryDBMap, - true, null, null, null)); + getCitationMarker2(style, + Collections.singletonList(entry), entryDBMap, + true, null, null, null)); assertEquals("Boström et al. [2006]", - style.getCitationMarker(Collections.singletonList(entry), entryDBMap, false, null, new int[]{3})); - assertEquals("Boström et al. [2006]", - getCitationMarker2(style, - Collections.singletonList(entry), entryDBMap, - false, null, new Boolean[]{false}, null)); + getCitationMarker2(style, + Collections.singletonList(entry), entryDBMap, + false, null, new Boolean[] {false}, null)); assertEquals("[Boström, Wäyrynen, Bodén, Beznosov & Kruchten, 2006]", - style.getCitationMarker(Collections.singletonList(entry), entryDBMap, true, null, new int[]{5})); - assertEquals("[Boström, Wäyrynen, Bodén, Beznosov & Kruchten, 2006]", - getCitationMarker2(style, - Collections.singletonList(entry), entryDBMap, - true, - null, - new Boolean[]{true} /* corresponds to -1, not 5 */, - null)); + getCitationMarker2(style, + Collections.singletonList(entry), entryDBMap, + true, + null, + new Boolean[] {true}, + null)); } @Test @@ -398,11 +367,10 @@ void testInstitutionAuthorMarker() throws IOException { database.insertEntry(entry); entries.add(entry); entryDBMap.put(entry, database); - assertEquals("[JabRef Development Team, 2016]", style.getCitationMarker(entries, entryDBMap, true, null, null)); assertEquals("[JabRef Development Team, 2016]", - getCitationMarker2(style, - entries, entryDBMap, true, null, null, null)); + getCitationMarker2(style, + entries, entryDBMap, true, null, null, null)); } @Test @@ -419,10 +387,11 @@ void testVonAuthorMarker() throws IOException { entry.setField(StandardField.AUTHOR, "Alpha von Beta"); entry.setField(StandardField.TITLE, "JabRef Manual"); entry.setField(StandardField.YEAR, "2016"); + entry.setCitationKey("a1"); database.insertEntry(entry); entries.add(entry); entryDBMap.put(entry, database); - assertEquals("[von Beta, 2016]", style.getCitationMarker(entries, entryDBMap, true, null, null)); + assertEquals("[von Beta, 2016]", getCitationMarker2(style, entries, entryDBMap, true, null, null, null)); } @Test @@ -437,10 +406,11 @@ void testNullAuthorMarker() throws IOException { BibEntry entry = new BibEntry(); entry.setType(StandardEntryType.Article); entry.setField(StandardField.YEAR, "2016"); + entry.setCitationKey("a1"); database.insertEntry(entry); entries.add(entry); entryDBMap.put(entry, database); - assertEquals("[, 2016]", style.getCitationMarker(entries, entryDBMap, true, null, null)); + assertEquals("[, 2016]", getCitationMarker2(style, entries, entryDBMap, true, null, null, null)); } @Test @@ -455,10 +425,11 @@ void testNullYearMarker() throws IOException { BibEntry entry = new BibEntry(); entry.setType(StandardEntryType.Article); entry.setField(StandardField.AUTHOR, "Alpha von Beta"); + entry.setCitationKey("a1"); database.insertEntry(entry); entries.add(entry); entryDBMap.put(entry, database); - assertEquals("[von Beta, ]", style.getCitationMarker(entries, entryDBMap, true, null, null)); + assertEquals("[von Beta, ]", getCitationMarker2(style, entries, entryDBMap, true, null, null, null)); } @Test @@ -472,10 +443,11 @@ void testEmptyEntryMarker() throws IOException { BibEntry entry = new BibEntry(); entry.setType(StandardEntryType.Article); + entry.setCitationKey("a1"); database.insertEntry(entry); entries.add(entry); entryDBMap.put(entry, database); - assertEquals("[, ]", style.getCitationMarker(entries, entryDBMap, true, null, null)); + assertEquals("[, ]", getCitationMarker2(style, entries, entryDBMap, true, null, null, null)); } @Test @@ -491,17 +463,20 @@ void testGetCitationMarkerInParenthesisUniquefiers() throws IOException { entry1.setField(StandardField.AUTHOR, "Alpha Beta"); entry1.setField(StandardField.TITLE, "Paper 1"); entry1.setField(StandardField.YEAR, "2000"); + entry1.setCitationKey("a1"); entries.add(entry1); database.insertEntry(entry1); BibEntry entry3 = new BibEntry(); entry3.setField(StandardField.AUTHOR, "Alpha Beta"); entry3.setField(StandardField.TITLE, "Paper 2"); entry3.setField(StandardField.YEAR, "2000"); + entry3.setCitationKey("a3"); entries.add(entry3); database.insertEntry(entry3); BibEntry entry2 = new BibEntry(); entry2.setField(StandardField.AUTHOR, "Gamma Epsilon"); entry2.setField(StandardField.YEAR, "2001"); + entry2.setCitationKey("a2"); entries.add(entry2); database.insertEntry(entry2); for (BibEntry entry : database.getEntries()) { @@ -509,9 +484,12 @@ void testGetCitationMarkerInParenthesisUniquefiers() throws IOException { } assertEquals("[Beta, 2000; Beta, 2000; Epsilon, 2001]", - style.getCitationMarker(entries, entryDBMap, true, null, null)); + getCitationMarker2b(style, entries, entryDBMap, true, null, null, null)); assertEquals("[Beta, 2000a,b; Epsilon, 2001]", - style.getCitationMarker(entries, entryDBMap, true, new String[]{"a", "b", ""}, new int[]{1, 1, 1})); + getCitationMarker2(style, entries, entryDBMap, true, + new String[] {"a", "b", ""}, + new Boolean[] {false, false, false}, + null)); } @Test @@ -527,27 +505,33 @@ void testGetCitationMarkerInTextUniquefiers() throws IOException { entry1.setField(StandardField.AUTHOR, "Alpha Beta"); entry1.setField(StandardField.TITLE, "Paper 1"); entry1.setField(StandardField.YEAR, "2000"); + entry1.setCitationKey("a1"); entries.add(entry1); database.insertEntry(entry1); BibEntry entry3 = new BibEntry(); entry3.setField(StandardField.AUTHOR, "Alpha Beta"); entry3.setField(StandardField.TITLE, "Paper 2"); entry3.setField(StandardField.YEAR, "2000"); + entry3.setCitationKey("a3"); entries.add(entry3); database.insertEntry(entry3); BibEntry entry2 = new BibEntry(); entry2.setField(StandardField.AUTHOR, "Gamma Epsilon"); entry2.setField(StandardField.YEAR, "2001"); entries.add(entry2); + entry2.setCitationKey("a2"); database.insertEntry(entry2); for (BibEntry entry : database.getEntries()) { entryDBMap.put(entry, database); } assertEquals("Beta [2000]; Beta [2000]; Epsilon [2001]", - style.getCitationMarker(entries, entryDBMap, false, null, null)); + getCitationMarker2b(style, entries, entryDBMap, false, null, null, null)); assertEquals("Beta [2000a,b]; Epsilon [2001]", - style.getCitationMarker(entries, entryDBMap, false, new String[]{"a", "b", ""}, new int[]{1, 1, 1})); + getCitationMarker2(style, entries, entryDBMap, false, + new String[] {"a", "b", ""}, + new Boolean[] {false, false, false}, + null)); } @Test @@ -563,18 +547,21 @@ void testGetCitationMarkerInParenthesisUniquefiersThreeSameAuthor() throws IOExc entry1.setField(StandardField.AUTHOR, "Alpha Beta"); entry1.setField(StandardField.TITLE, "Paper 1"); entry1.setField(StandardField.YEAR, "2000"); + entry1.setCitationKey("a1"); entries.add(entry1); database.insertEntry(entry1); BibEntry entry2 = new BibEntry(); entry2.setField(StandardField.AUTHOR, "Alpha Beta"); entry2.setField(StandardField.TITLE, "Paper 2"); entry2.setField(StandardField.YEAR, "2000"); + entry2.setCitationKey("a2"); entries.add(entry2); database.insertEntry(entry2); BibEntry entry3 = new BibEntry(); entry3.setField(StandardField.AUTHOR, "Alpha Beta"); entry3.setField(StandardField.TITLE, "Paper 3"); entry3.setField(StandardField.YEAR, "2000"); + entry3.setCitationKey("a3"); entries.add(entry3); database.insertEntry(entry3); for (BibEntry entry : database.getEntries()) { @@ -582,7 +569,10 @@ void testGetCitationMarkerInParenthesisUniquefiersThreeSameAuthor() throws IOExc } assertEquals("[Beta, 2000a,b,c]", - style.getCitationMarker(entries, entryDBMap, true, new String[]{"a", "b", "c"}, new int[]{1, 1, 1})); + getCitationMarker2(style, entries, entryDBMap, true, + new String[] {"a", "b", "c"}, + new Boolean[] {false, false, false}, + null)); } @Test @@ -598,18 +588,21 @@ void testGetCitationMarkerInTextUniquefiersThreeSameAuthor() throws IOException entry1.setField(StandardField.AUTHOR, "Alpha Beta"); entry1.setField(StandardField.TITLE, "Paper 1"); entry1.setField(StandardField.YEAR, "2000"); + entry1.setCitationKey("a1"); entries.add(entry1); database.insertEntry(entry1); BibEntry entry2 = new BibEntry(); entry2.setField(StandardField.AUTHOR, "Alpha Beta"); entry2.setField(StandardField.TITLE, "Paper 2"); entry2.setField(StandardField.YEAR, "2000"); + entry2.setCitationKey("a2"); entries.add(entry2); database.insertEntry(entry2); BibEntry entry3 = new BibEntry(); entry3.setField(StandardField.AUTHOR, "Alpha Beta"); entry3.setField(StandardField.TITLE, "Paper 3"); entry3.setField(StandardField.YEAR, "2000"); + entry3.setCitationKey("a3"); entries.add(entry3); database.insertEntry(entry3); for (BibEntry entry : database.getEntries()) { @@ -617,11 +610,14 @@ void testGetCitationMarkerInTextUniquefiersThreeSameAuthor() throws IOException } assertEquals("Beta [2000a,b,c]", - style.getCitationMarker(entries, entryDBMap, false, new String[]{"a", "b", "c"}, new int[]{1, 1, 1})); + getCitationMarker2(style, entries, entryDBMap, false, + new String[] {"a", "b", "c"}, + new Boolean[] {false, false, false}, + null)); } @Test - // TODO: equals only work when initialized from file, not from reader + // TODO: equals only work when initialized from file, not from reader void testEquals() throws IOException { OOBibStyle style1 = new OOBibStyle(StyleLoader.DEFAULT_NUMERICAL_STYLE_PATH, layoutFormatterPreferences); @@ -631,7 +627,7 @@ void testEquals() throws IOException { } @Test - // TODO: equals only work when initialized from file, not from reader + // TODO: equals only work when initialized from file, not from reader void testNotEquals() throws IOException { OOBibStyle style1 = new OOBibStyle(StyleLoader.DEFAULT_NUMERICAL_STYLE_PATH, layoutFormatterPreferences); @@ -671,11 +667,12 @@ void testEmptyStringPropertyAndOxfordComma() throws Exception { entry.setField(StandardField.AUTHOR, "Alpha von Beta and Gamma Epsilon and Ypsilon Tau"); entry.setField(StandardField.TITLE, "JabRef Manual"); entry.setField(StandardField.YEAR, "2016"); + entry.setCitationKey("a1"); database.insertEntry(entry); entries.add(entry); entryDBMap.put(entry, database); assertEquals("von Beta, Epsilon, & Tau, 2016", - style.getCitationMarker(entries, entryDBMap, true, null, null)); + getCitationMarker2(style, entries, entryDBMap, true, null, null, null)); } @Test @@ -687,20 +684,20 @@ void testIsValidWithDefaultSectionAtTheStart() throws Exception { @Test void testGetCitationMarkerJoinFirst() throws IOException { OOBibStyle style = new OOBibStyle(StyleLoader.DEFAULT_NUMERICAL_STYLE_PATH, - layoutFormatterPreferences); + layoutFormatterPreferences); - // Question: What should happen if some of the sources is + // Question: What should happen if some sources are // marked as isFirstAppearanceOfSource? // This test documents what is happening now. // Two entries with identical normalizedMarkers and many authors. BibEntry entry1 = new BibEntry() .withField(StandardField.AUTHOR, - "Gustav Bostr\\\"{o}m" - + " and Jaana W\\\"{a}yrynen" - + " and Marine Bod\\'{e}n" - + " and Konstantin Beznosov" - + " and Philippe Kruchten") + "Gustav Bostr\\\"{o}m" + + " and Jaana W\\\"{a}yrynen" + + " and Marine Bod\\'{e}n" + + " and Konstantin Beznosov" + + " and Philippe Kruchten") .withField(StandardField.YEAR, "2006") .withField(StandardField.BOOKTITLE, "A book 1") .withField(StandardField.PUBLISHER, "ACM") @@ -710,11 +707,11 @@ void testGetCitationMarkerJoinFirst() throws IOException { BibEntry entry2 = new BibEntry() .withField(StandardField.AUTHOR, - "Gustav Bostr\\\"{o}m" - + " and Jaana W\\\"{a}yrynen" - + " and Marine Bod\\'{e}n" - + " and Konstantin Beznosov" - + " and Philippe Kruchten") + "Gustav Bostr\\\"{o}m" + + " and Jaana W\\\"{a}yrynen" + + " and Marine Bod\\'{e}n" + + " and Konstantin Beznosov" + + " and Philippe Kruchten") .withField(StandardField.YEAR, "2006") .withField(StandardField.BOOKTITLE, "A book 2") .withField(StandardField.PUBLISHER, "ACM") @@ -725,11 +722,11 @@ void testGetCitationMarkerJoinFirst() throws IOException { // Last Author differs. BibEntry entry3 = new BibEntry() .withField(StandardField.AUTHOR, - "Gustav Bostr\\\"{o}m" - + " and Jaana W\\\"{a}yrynen" - + " and Marine Bod\\'{e}n" - + " and Konstantin Beznosov" - + " and Philippe NotKruchten") + "Gustav Bostr\\\"{o}m" + + " and Jaana W\\\"{a}yrynen" + + " and Marine Bod\\'{e}n" + + " and Konstantin Beznosov" + + " and Philippe NotKruchten") .withField(StandardField.YEAR, "2006") .withField(StandardField.BOOKTITLE, "A book 3") .withField(StandardField.PUBLISHER, "ACM") @@ -747,26 +744,26 @@ void testGetCitationMarkerJoinFirst() throws IOException { if (true) { List citationMarkerEntries = new ArrayList<>(); CitationMarkerEntry cm1 = - makeCitationMarkerEntry(entry1, database, "a", null, true); + makeCitationMarkerEntry(entry1, database, "a", null, true); citationMarkerEntries.add(cm1); CitationMarkerEntry cm2 = - makeCitationMarkerEntry(entry2, database, "b", null, true); + makeCitationMarkerEntry(entry2, database, "b", null, true); citationMarkerEntries.add(cm2); CitationMarkerEntry cm3 = - makeCitationMarkerEntry(entry3, database, "c", null, true); + makeCitationMarkerEntry(entry3, database, "c", null, true); citationMarkerEntries.add(cm3); assertEquals("[Boström, Wäyrynen, Bodén, Beznosov & Kruchten, 2006a,b" - + "; Boström, Wäyrynen, Bodén, Beznosov & NotKruchten, 2006c]", - style.createCitationMarker(citationMarkerEntries, - true, - NonUniqueCitationMarker.THROWS).toString()); + + "; Boström, Wäyrynen, Bodén, Beznosov & NotKruchten, 2006c]", + style.createCitationMarker(citationMarkerEntries, + true, + NonUniqueCitationMarker.THROWS).toString()); assertEquals("Boström, Wäyrynen, Bodén, Beznosov & Kruchten [2006a,b]" - + "; Boström, Wäyrynen, Bodén, Beznosov & NotKruchten [2006c]", - style.createCitationMarker(citationMarkerEntries, - false, - NonUniqueCitationMarker.THROWS).toString()); + + "; Boström, Wäyrynen, Bodén, Beznosov & NotKruchten [2006c]", + style.createCitationMarker(citationMarkerEntries, + false, + NonUniqueCitationMarker.THROWS).toString()); } // Without pageInfo, only the first is isFirstAppearanceOfSource. @@ -775,21 +772,20 @@ void testGetCitationMarkerJoinFirst() throws IOException { if (true) { List citationMarkerEntries = new ArrayList<>(); CitationMarkerEntry cm1 = - makeCitationMarkerEntry(entry1, database, "a", null, true); + makeCitationMarkerEntry(entry1, database, "a", null, true); citationMarkerEntries.add(cm1); CitationMarkerEntry cm2 = - makeCitationMarkerEntry(entry2, database, "b", null, false); + makeCitationMarkerEntry(entry2, database, "b", null, false); citationMarkerEntries.add(cm2); CitationMarkerEntry cm3 = - makeCitationMarkerEntry(entry3, database, "c", null, false); + makeCitationMarkerEntry(entry3, database, "c", null, false); citationMarkerEntries.add(cm3); assertEquals("[Boström, Wäyrynen, Bodén, Beznosov & Kruchten, 2006a,b" - + "; Boström et al., 2006c]", - style.createCitationMarker(citationMarkerEntries, - true, - NonUniqueCitationMarker.THROWS).toString()); - + + "; Boström et al., 2006c]", + style.createCitationMarker(citationMarkerEntries, + true, + NonUniqueCitationMarker.THROWS).toString()); } // Without pageInfo, only the second is isFirstAppearanceOfSource. // The second is not joined, because it is a first appearance, thus @@ -798,21 +794,21 @@ void testGetCitationMarkerJoinFirst() throws IOException { if (true) { List citationMarkerEntries = new ArrayList<>(); CitationMarkerEntry cm1 = - makeCitationMarkerEntry(entry1, database, "a", null, false); + makeCitationMarkerEntry(entry1, database, "a", null, false); citationMarkerEntries.add(cm1); CitationMarkerEntry cm2 = - makeCitationMarkerEntry(entry2, database, "b", null, true); + makeCitationMarkerEntry(entry2, database, "b", null, true); citationMarkerEntries.add(cm2); CitationMarkerEntry cm3 = - makeCitationMarkerEntry(entry3, database, "c", null, false); + makeCitationMarkerEntry(entry3, database, "c", null, false); citationMarkerEntries.add(cm3); assertEquals("[Boström et al., 2006a" - + "; Boström, Wäyrynen, Bodén, Beznosov & Kruchten, 2006b" - + "; Boström et al., 2006c]", - style.createCitationMarker(citationMarkerEntries, - true, - NonUniqueCitationMarker.THROWS).toString()); + + "; Boström, Wäyrynen, Bodén, Beznosov & Kruchten, 2006b" + + "; Boström et al., 2006c]", + style.createCitationMarker(citationMarkerEntries, + true, + NonUniqueCitationMarker.THROWS).toString()); } // Without pageInfo, neither is isFirstAppearanceOfSource. @@ -822,19 +818,19 @@ void testGetCitationMarkerJoinFirst() throws IOException { if (true) { List citationMarkerEntries = new ArrayList<>(); CitationMarkerEntry cm1 = - makeCitationMarkerEntry(entry1, database, "a", null, false); + makeCitationMarkerEntry(entry1, database, "a", null, false); citationMarkerEntries.add(cm1); CitationMarkerEntry cm2 = - makeCitationMarkerEntry(entry2, database, "b", null, false); + makeCitationMarkerEntry(entry2, database, "b", null, false); citationMarkerEntries.add(cm2); CitationMarkerEntry cm3 = - makeCitationMarkerEntry(entry3, database, "c", null, false); + makeCitationMarkerEntry(entry3, database, "c", null, false); citationMarkerEntries.add(cm3); assertEquals("[Boström et al., 2006a,b,c]", - style.createCitationMarker(citationMarkerEntries, - true, - NonUniqueCitationMarker.THROWS).toString()); + style.createCitationMarker(citationMarkerEntries, + true, + NonUniqueCitationMarker.THROWS).toString()); } // With pageInfo: different entries with identical non-null pageInfo: not joined. @@ -842,21 +838,21 @@ void testGetCitationMarkerJoinFirst() throws IOException { if (true) { List citationMarkerEntries = new ArrayList<>(); CitationMarkerEntry cm1 = - makeCitationMarkerEntry(entry1, database, "a", "p1", false); + makeCitationMarkerEntry(entry1, database, "a", "p1", false); citationMarkerEntries.add(cm1); CitationMarkerEntry cm2 = - makeCitationMarkerEntry(entry2, database, "b", "p1", false); + makeCitationMarkerEntry(entry2, database, "b", "p1", false); citationMarkerEntries.add(cm2); CitationMarkerEntry cm3 = - makeCitationMarkerEntry(entry3, database, "c", "p1", false); + makeCitationMarkerEntry(entry3, database, "c", "p1", false); citationMarkerEntries.add(cm3); assertEquals("[Boström et al., 2006a; p1" - + "; Boström et al., 2006b; p1" - + "; Boström et al., 2006c; p1]", - style.createCitationMarker(citationMarkerEntries, - true, - NonUniqueCitationMarker.THROWS).toString()); + + "; Boström et al., 2006b; p1" + + "; Boström et al., 2006c; p1]", + style.createCitationMarker(citationMarkerEntries, + true, + NonUniqueCitationMarker.THROWS).toString()); } // With pageInfo: same entries with identical non-null pageInfo: collapsed. @@ -865,43 +861,43 @@ void testGetCitationMarkerJoinFirst() throws IOException { if (true) { List citationMarkerEntries = new ArrayList<>(); CitationMarkerEntry cm1 = - makeCitationMarkerEntry(entry1, database, "a", "p1", false); + makeCitationMarkerEntry(entry1, database, "a", "p1", false); citationMarkerEntries.add(cm1); CitationMarkerEntry cm2 = - makeCitationMarkerEntry(entry1, database, "a", "p1", false); + makeCitationMarkerEntry(entry1, database, "a", "p1", false); citationMarkerEntries.add(cm2); CitationMarkerEntry cm3 = - makeCitationMarkerEntry(entry1, database, "a", "p1", false); + makeCitationMarkerEntry(entry1, database, "a", "p1", false); citationMarkerEntries.add(cm3); assertEquals("[Boström et al., 2006a; p1]", - style.createCitationMarker(citationMarkerEntries, - true, - NonUniqueCitationMarker.THROWS).toString()); + style.createCitationMarker(citationMarkerEntries, + true, + NonUniqueCitationMarker.THROWS).toString()); } // With pageInfo: same entries with different pageInfo: kept separate. // Empty ("") and missing pageInfos considered equal, thus collapsed. if (true) { List citationMarkerEntries = new ArrayList<>(); CitationMarkerEntry cm1 = - makeCitationMarkerEntry(entry1, database, "a", "p1", false); + makeCitationMarkerEntry(entry1, database, "a", "p1", false); citationMarkerEntries.add(cm1); CitationMarkerEntry cm2 = - makeCitationMarkerEntry(entry1, database, "a", "p2", false); + makeCitationMarkerEntry(entry1, database, "a", "p2", false); citationMarkerEntries.add(cm2); CitationMarkerEntry cm3 = - makeCitationMarkerEntry(entry1, database, "a", "", false); + makeCitationMarkerEntry(entry1, database, "a", "", false); citationMarkerEntries.add(cm3); CitationMarkerEntry cm4 = - makeCitationMarkerEntry(entry1, database, "a", null, false); + makeCitationMarkerEntry(entry1, database, "a", null, false); citationMarkerEntries.add(cm4); assertEquals("[Boström et al., 2006a; p1" - + "; Boström et al., 2006a; p2" - + "; Boström et al., 2006a]", - style.createCitationMarker(citationMarkerEntries, - true, - NonUniqueCitationMarker.THROWS).toString()); + + "; Boström et al., 2006a; p2" + + "; Boström et al., 2006a]", + style.createCitationMarker(citationMarkerEntries, + true, + NonUniqueCitationMarker.THROWS).toString()); } } } diff --git a/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTestHelper.java b/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTestHelper.java index ae5e83abd2f..4ab0c6ef5d3 100644 --- a/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTestHelper.java +++ b/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTestHelper.java @@ -1,6 +1,5 @@ package org.jabref.logic.openoffice.style; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -23,10 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; class OOBibStyleTestHelper { - /* - * begin Helpers for testing style.getNumCitationMarker2 - */ - /* * Minimal implementation for CitationMarkerNumericEntry */ @@ -37,15 +32,15 @@ static class CitationMarkerNumericEntryImpl implements CitationMarkerNumericEntr */ public final static int UNRESOLVED_ENTRY_NUMBER = 0; - private String citationKey; - private Optional num; - private Optional pageInfo; + private final String citationKey; + private final Optional num; + private final Optional pageInfo; public CitationMarkerNumericEntryImpl(String citationKey, int num, Optional pageInfo) { this.citationKey = citationKey; this.num = (num == UNRESOLVED_ENTRY_NUMBER - ? Optional.empty() - : Optional.of(num)); + ? Optional.empty() + : Optional.of(num)); this.pageInfo = PageInfo.normalizePageInfo(pageInfo); } @@ -98,22 +93,21 @@ static String runGetNumCitationMarker2a(OOBibStyle style, List num, int minGroupingCount, boolean inList) { if (inList) { if (num.size() != 1) { - throw new IllegalArgumentException("Numeric label for the bibliography with " - + String.valueOf(num.size()) + " numbers?"); + throw new IllegalArgumentException("Numeric label for the bibliography with " + num.size() + " numbers?"); } int n = num.get(0); CitationMarkerNumericBibEntryImpl x = - new CitationMarkerNumericBibEntryImpl("key", - (n == 0) ? Optional.empty() : Optional.of(n)); + new CitationMarkerNumericBibEntryImpl("key", + (n == 0) ? Optional.empty() : Optional.of(n)); return style.getNumCitationMarkerForBibliography(x).toString(); } else { List input = - num.stream() - .map(n -> - new CitationMarkerNumericEntryImpl("key" + String.valueOf(n), - n, - Optional.empty())) - .collect(Collectors.toList()); + num.stream() + .map(n -> + new CitationMarkerNumericEntryImpl("key" + n, + n, + Optional.empty())) + .collect(Collectors.toList()); return style.getNumCitationMarker2(input, minGroupingCount).toString(); } } @@ -163,13 +157,14 @@ static CitationMarkerEntry makeCitationMarkerEntry(BibEntry entry, * Similar to old API. pageInfo is new, and unlimAuthors is * replaced with isFirstAppearanceOfSource */ - static String getCitationMarker2(OOBibStyle style, - List entries, - Map entryDBMap, - boolean inParenthesis, - String[] uniquefiers, - Boolean[] isFirstAppearanceOfSource, - String[] pageInfo) { + static String getCitationMarker2ab(OOBibStyle style, + List entries, + Map entryDBMap, + boolean inParenthesis, + String[] uniquefiers, + Boolean[] isFirstAppearanceOfSource, + String[] pageInfo, + NonUniqueCitationMarker nonunique) { if (uniquefiers == null) { uniquefiers = new String[entries.size()]; Arrays.fill(uniquefiers, null); @@ -186,151 +181,185 @@ static String getCitationMarker2(OOBibStyle style, for (int i = 0; i < entries.size(); i++) { BibEntry entry = entries.get(i); CitationMarkerEntry e = makeCitationMarkerEntry(entry, - entryDBMap.get(entry), - uniquefiers[i], - pageInfo[i], - isFirstAppearanceOfSource[i]); + entryDBMap.get(entry), + uniquefiers[i], + pageInfo[i], + isFirstAppearanceOfSource[i]); citationMarkerEntries.add(e); } return style.createCitationMarker(citationMarkerEntries, - inParenthesis, - NonUniqueCitationMarker.THROWS).toString(); + inParenthesis, + nonunique).toString(); + } + + static String getCitationMarker2(OOBibStyle style, + List entries, + Map entryDBMap, + boolean inParenthesis, + String[] uniquefiers, + Boolean[] isFirstAppearanceOfSource, + String[] pageInfo) { + return getCitationMarker2ab(style, + entries, + entryDBMap, + inParenthesis, + uniquefiers, + isFirstAppearanceOfSource, + pageInfo, + NonUniqueCitationMarker.THROWS); + } + + static String getCitationMarker2b(OOBibStyle style, + List entries, + Map entryDBMap, + boolean inParenthesis, + String[] uniquefiers, + Boolean[] isFirstAppearanceOfSource, + String[] pageInfo) { + return getCitationMarker2ab(style, + entries, + entryDBMap, + inParenthesis, + uniquefiers, + isFirstAppearanceOfSource, + pageInfo, + NonUniqueCitationMarker.FORGIVEN); } /* * end helper */ - static void testGetNumCitationMarkerExtra(OOBibStyle style) throws IOException { + static void testGetNumCitationMarkerExtra(OOBibStyle style) { // Identical numeric entries are joined. assertEquals("[1; 2]", runGetNumCitationMarker2b(style, 3, - numEntry("x1", 1, null), - numEntry("x2", 2, null), - numEntry("x1", 2, null), - numEntry("x2", 1, null))); + numEntry("x1", 1, null), + numEntry("x2", 2, null), + numEntry("x1", 2, null), + numEntry("x2", 1, null))); // ... unless minGroupingCount <= 0 assertEquals("[1; 1; 2; 2]", runGetNumCitationMarker2b(style, 0, - numEntry("x1", 1, null), - numEntry("x2", 2, null), - numEntry("x1", 2, null), - numEntry("x2", 1, null))); + numEntry("x1", 1, null), + numEntry("x2", 2, null), + numEntry("x1", 2, null), + numEntry("x2", 1, null))); // ... or have different pageInfos assertEquals("[1; p1a; 1; p1b; 2; p2; 3]", runGetNumCitationMarker2b(style, 1, - numEntry("x1", 1, "p1a"), - numEntry("x1", 1, "p1b"), - numEntry("x2", 2, "p2"), - numEntry("x2", 2, "p2"), - numEntry("x3", 3, null), - numEntry("x3", 3, null))); + numEntry("x1", 1, "p1a"), + numEntry("x1", 1, "p1b"), + numEntry("x2", 2, "p2"), + numEntry("x2", 2, "p2"), + numEntry("x3", 3, null), + numEntry("x3", 3, null))); // Consecutive numbers can become a range ... assertEquals("[1-3]", runGetNumCitationMarker2b(style, 1, - numEntry("x1", 1, null), - numEntry("x2", 2, null), - numEntry("x3", 3, null))); + numEntry("x1", 1, null), + numEntry("x2", 2, null), + numEntry("x3", 3, null))); // ... unless minGroupingCount is too high assertEquals("[1; 2; 3]", runGetNumCitationMarker2b(style, 4, - numEntry("x1", 1, null), - numEntry("x2", 2, null), - numEntry("x3", 3, null))); + numEntry("x1", 1, null), + numEntry("x2", 2, null), + numEntry("x3", 3, null))); // ... or if minGroupingCount <= 0 assertEquals("[1; 2; 3]", runGetNumCitationMarker2b(style, 0, - numEntry("x1", 1, null), - numEntry("x2", 2, null), - numEntry("x3", 3, null))); + numEntry("x1", 1, null), + numEntry("x2", 2, null), + numEntry("x3", 3, null))); // ... a pageInfo needs to be emitted assertEquals("[1; p1; 2-3]", runGetNumCitationMarker2b(style, 1, - numEntry("x1", 1, "p1"), - numEntry("x2", 2, null), - numEntry("x3", 3, null))); + numEntry("x1", 1, "p1"), + numEntry("x2", 2, null), + numEntry("x3", 3, null))); // null and "" pageInfos are taken as equal. // Due to trimming, " " is the same as well. assertEquals("[1]", runGetNumCitationMarker2b(style, 1, - numEntry("x1", 1, ""), - numEntry("x1", 1, null), - numEntry("x1", 1, " "))); + numEntry("x1", 1, ""), + numEntry("x1", 1, null), + numEntry("x1", 1, " "))); // pageInfos are trimmed assertEquals("[1; p1]", runGetNumCitationMarker2b(style, 1, - numEntry("x1", 1, "p1"), - numEntry("x1", 1, " p1"), - numEntry("x1", 1, "p1 "))); + numEntry("x1", 1, "p1"), + numEntry("x1", 1, " p1"), + numEntry("x1", 1, "p1 "))); // The citation numbers come out sorted assertEquals("[3-5; 7; 10-12]", runGetNumCitationMarker2b(style, 1, - numEntry("x12", 12, null), - numEntry("x7", 7, null), - numEntry("x3", 3, null), - numEntry("x4", 4, null), - numEntry("x11", 11, null), - numEntry("x10", 10, null), - numEntry("x5", 5, null))); + numEntry("x12", 12, null), + numEntry("x7", 7, null), + numEntry("x3", 3, null), + numEntry("x4", 4, null), + numEntry("x11", 11, null), + numEntry("x10", 10, null), + numEntry("x5", 5, null))); // pageInfos are sorted together with the numbers // (but they inhibit ranges where they are, even if they are identical, // but not empty-or-null) assertEquals("[3; p3; 4; p4; 5; p5; 7; p7; 10; px; 11; px; 12; px]", - runGetNumCitationMarker2b(style, 1, - numEntry("x12", 12, "px"), - numEntry("x7", 7, "p7"), - numEntry("x3", 3, "p3"), - numEntry("x4", 4, "p4"), - numEntry("x11", 11, "px"), - numEntry("x10", 10, "px"), - numEntry("x5", 5, "p5"))); + runGetNumCitationMarker2b(style, 1, + numEntry("x12", 12, "px"), + numEntry("x7", 7, "p7"), + numEntry("x3", 3, "p3"), + numEntry("x4", 4, "p4"), + numEntry("x11", 11, "px"), + numEntry("x10", 10, "px"), + numEntry("x5", 5, "p5"))); // pageInfo sorting (for the same number) assertEquals("[1; 1; a; 1; b]", - runGetNumCitationMarker2b(style, 1, - numEntry("x1", 1, ""), - numEntry("x1", 1, "b"), - numEntry("x1", 1, "a"))); + runGetNumCitationMarker2b(style, 1, + numEntry("x1", 1, ""), + numEntry("x1", 1, "b"), + numEntry("x1", 1, "a"))); // pageInfo sorting (for the same number) is not numeric. assertEquals("[1; p100; 1; p20; 1; p9]", - runGetNumCitationMarker2b(style, 1, - numEntry("x1", 1, "p20"), - numEntry("x1", 1, "p9"), - numEntry("x1", 1, "p100"))); + runGetNumCitationMarker2b(style, 1, + numEntry("x1", 1, "p20"), + numEntry("x1", 1, "p9"), + numEntry("x1", 1, "p100"))); assertEquals("[1-3]", - runGetNumCitationMarker2b(style, 1, - numEntry("x1", 1, null), - numEntry("x2", 2, null), - numEntry("x3", 3, null))); + runGetNumCitationMarker2b(style, 1, + numEntry("x1", 1, null), + numEntry("x2", 2, null), + numEntry("x3", 3, null))); assertEquals("[1; 2; 3]", - runGetNumCitationMarker2b(style, 5, - numEntry("x1", 1, null), - numEntry("x2", 2, null), - numEntry("x3", 3, null))); + runGetNumCitationMarker2b(style, 5, + numEntry("x1", 1, null), + numEntry("x2", 2, null), + numEntry("x3", 3, null))); assertEquals("[1; 2; 3]", - runGetNumCitationMarker2b(style, -1, - numEntry("x1", 1, null), - numEntry("x2", 2, null), - numEntry("x3", 3, null))); + runGetNumCitationMarker2b(style, -1, + numEntry("x1", 1, null), + numEntry("x2", 2, null), + numEntry("x3", 3, null))); assertEquals("[1; 3; 12]", - runGetNumCitationMarker2b(style, 1, - numEntry("x1", 1, null), - numEntry("x12", 12, null), - numEntry("x3", 3, null))); + runGetNumCitationMarker2b(style, 1, + numEntry("x1", 1, null), + numEntry("x12", 12, null), + numEntry("x3", 3, null))); assertEquals("[3-5; 7; 10-12]", - runGetNumCitationMarker2b(style, 1, - numEntry("x12", 12, ""), - numEntry("x7", 7, ""), - numEntry("x3", 3, ""), - numEntry("x4", 4, ""), - numEntry("x11", 11, ""), - numEntry("x10", 10, ""), - numEntry("x5", 5, ""))); + runGetNumCitationMarker2b(style, 1, + numEntry("x12", 12, ""), + numEntry("x7", 7, ""), + numEntry("x3", 3, ""), + numEntry("x4", 4, ""), + numEntry("x11", 11, ""), + numEntry("x10", 10, ""), + numEntry("x5", 5, ""))); } } diff --git a/src/test/java/org/jabref/logic/openoffice/style/StyleLoaderTest.java b/src/test/java/org/jabref/logic/openoffice/style/StyleLoaderTest.java index 5f5b0884717..50382895b56 100644 --- a/src/test/java/org/jabref/logic/openoffice/style/StyleLoaderTest.java +++ b/src/test/java/org/jabref/logic/openoffice/style/StyleLoaderTest.java @@ -79,7 +79,7 @@ public void testAddInvalidStyleLeadsToNoMoreStyle() { @Test public void testInitalizeWithOneExternalFile() throws URISyntaxException { String filename = Path.of(StyleLoader.class.getResource(StyleLoader.DEFAULT_AUTHORYEAR_STYLE_PATH).toURI()) - .toFile().getPath(); + .toFile().getPath(); when(preferences.getExternalStyles()).thenReturn(Collections.singletonList(filename)); loader = new StyleLoader(preferences, layoutPreferences); assertEquals(NUMBER_OF_INTERNAL_STYLES + 1, loader.getStyles().size()); @@ -96,7 +96,7 @@ public void testInitalizeWithIncorrectExternalFile() { @Test public void testInitalizeWithOneExternalFileRemoveStyle() throws URISyntaxException { String filename = Path.of(StyleLoader.class.getResource(StyleLoader.DEFAULT_AUTHORYEAR_STYLE_PATH).toURI()) - .toFile().getPath(); + .toFile().getPath(); when(preferences.getExternalStyles()).thenReturn(Collections.singletonList(filename)); loader = new StyleLoader(preferences, layoutPreferences); @@ -117,7 +117,7 @@ public void testInitalizeWithOneExternalFileRemoveStyle() throws URISyntaxExcept @Test public void testInitalizeWithOneExternalFileRemoveStyleUpdatesPreferences() throws URISyntaxException { String filename = Path.of(StyleLoader.class.getResource(StyleLoader.DEFAULT_AUTHORYEAR_STYLE_PATH).toURI()) - .toFile().getPath(); + .toFile().getPath(); when(preferences.getExternalStyles()).thenReturn(Collections.singletonList(filename)); loader = new StyleLoader(preferences, layoutPreferences); @@ -141,7 +141,7 @@ public void testAddSameStyleTwiceLeadsToOneMoreStyle() throws URISyntaxException loader = new StyleLoader(preferences, layoutPreferences); int beforeAdding = loader.getStyles().size(); String filename = Path.of(StyleLoader.class.getResource(StyleLoader.DEFAULT_AUTHORYEAR_STYLE_PATH).toURI()) - .toFile().getPath(); + .toFile().getPath(); loader.addStyleIfValid(filename); loader.addStyleIfValid(filename); assertEquals(beforeAdding + 1, loader.getStyles().size()); diff --git a/src/test/java/org/jabref/model/openoffice/CitationEntryTest.java b/src/test/java/org/jabref/model/openoffice/CitationEntryTest.java index fd7ec48b9b3..8648d9b5df9 100644 --- a/src/test/java/org/jabref/model/openoffice/CitationEntryTest.java +++ b/src/test/java/org/jabref/model/openoffice/CitationEntryTest.java @@ -11,7 +11,7 @@ class CitationEntryTest { @Test - void testCitationEntryInitalPageInfo() { + void testCitationEntryInitialPageInfo() { CitationEntry citationEntry = new CitationEntry("RefMark", "Context", "Info"); assertTrue(citationEntry.getPageInfo().isPresent()); assertEquals("Info", citationEntry.getPageInfo().get()); @@ -20,7 +20,7 @@ void testCitationEntryInitalPageInfo() { } @Test - void testCitationEntryOptionalInitalPageInfo() { + void testCitationEntryOptionalInitialPageInfo() { CitationEntry citationEntry = new CitationEntry("RefMark", "Context", Optional.of("Info")); assertEquals(Optional.of("Info"), citationEntry.getPageInfo()); @@ -35,7 +35,7 @@ void testCitationEntryInitalPageInfoChanged() { } @Test - void testCitationEntryNoInitalPageInfo() { + void testCitationEntryNoInitialPageInfo() { CitationEntry citationEntry = new CitationEntry("RefMark", "Context"); assertEquals(Optional.empty(), citationEntry.getPageInfo()); }