Skip to content

refactor: PeriodToString & BoundsPeriod#378

Merged
jy95 merged 1 commit intomainfrom
PeriodToString
May 7, 2026
Merged

refactor: PeriodToString & BoundsPeriod#378
jy95 merged 1 commit intomainfrom
PeriodToString

Conversation

@jy95
Copy link
Copy Markdown
Owner

@jy95 jy95 commented May 7, 2026

Summary by CodeRabbit

  • Refactor
    • Restructured period display architecture to improve consistency in how time-bound information is presented across different data format versions.
    • Enhanced the handling of period start and end date formatting for more reliable and uniform display of timing information.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR extracts the period-to-string conversion logic from BoundsPeriod into a reusable PeriodToString interface, implements it for FHIR R4 and R5, refactors BoundsPeriod into a generic class using dependency injection, and updates translator wiring to inject version-specific implementations. The R4-specific BoundsPeriodR4 is removed.

Changes

BoundsPeriod Generic Abstraction Refactoring

Layer / File(s) Summary
Period Abstraction Contract
common/src/main/java/io/github/jy95/fds/common/functions/PeriodToString.java
New generic PeriodToString<P, D, C> interface with abstract methods for period field access (hasStart, hasEnd, getStart, getEnd) and date formatting (formatDateTime), plus default implementations for condition code derivation, information extraction, and async period-to-string conversion via CompletableFuture.
R4 & R5 Period Implementations
r4/src/main/java/io/github/jy95/fds/r4/functions/PeriodToStringR4.java, r5/src/main/java/io/github/jy95/fds/r5/functions/PeriodToStringR5.java
New enums PeriodToStringR4 and PeriodToStringR5 implementing PeriodToString for FHIR R4 and R5, delegating period field access to version-specific Period models and date formatting to TranslationService.dateTimeToHumanDisplay.
Generic Translator Implementation
common/src/main/java/io/github/jy95/fds/common/translators/timing/repeat/BoundsPeriod.java
BoundsPeriod refactored from interface to concrete generic class implementing Translator<D> with injected dependencies: TranslationService<C>, PeriodToString<P, ?, C>, Predicate<D> for presence checking, and Function<D, P> for period extraction.
Translator Wiring Updates
r4/src/main/java/io/github/jy95/fds/r4/utils/maps/TimingRepeatTranslatorsMapR4.java, r5/src/main/java/io/github/jy95/fds/r5/utils/maps/TimingRepeatTranslatorsMapR5.java, r5/src/main/java/io/github/jy95/fds/r5/translators/BoundsPeriodR5.java
Map files instantiate generic BoundsPeriod<> with version-specific PeriodToStringR4/PeriodToStringR5, translation service, and TimingRepeatComponent field accessors as method references.
Legacy Removal
r4/src/main/java/io/github/jy95/fds/r4/translators/BoundsPeriodR4.java
Remove R4-specific BoundsPeriodR4 class, replacing it with the generic BoundsPeriod wired with PeriodToStringR4.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • jy95/fds#372: Performs the same cross-cutting refactor pattern—replacing per-version translator interfaces/classes with reusable generic translator implementations and wiring R4/R5-specific accessors to them.
  • jy95/fds#370: Performs the same refactor pattern: converting specific timing-repeat translator interfaces into generic, constructor-injected classes (e.g., replacing per-version translators with shared generic implementations and introducing a PeriodToString abstraction).
  • jy95/fds#252: Directly related as this PR's refactor of BoundsPeriod and introduction of PeriodToString and R4/R5 implementations touch and modify the same translator components introduced/reorganized in that PR.

Poem

🐰 A period's tale, now generically told,
One contract to rule them, bold and controlled,
R4 and R5 dance in harmony's fold,
Translation abstracted, logic retold.
Bounds rebound with elegance untold!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main structural refactoring: extracting period-to-string conversion logic into a generic interface while reshaping BoundsPeriod from an interface to a concrete class.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch PeriodToString

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 7, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 14 complexity · 0 duplication

Metric Results
Complexity 14
Duplication 0

View in Codacy

🟢 Coverage 100.00% diff coverage · +0.00% coverage variation

Metric Results
Coverage variation +0.00% coverage variation (-1.00%)
Diff coverage 100.00% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (e70dd08) 1326 1326 100.00%
Head commit (cd2707a) 2630 (+1304) 2630 (+1304) 100.00% (+0.00%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#378) 47 47 100.00%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
common/src/main/java/io/github/jy95/fds/common/functions/PeriodToString.java (3)

103-110: 💤 Low value

Hardcoded translation key couples a generically-named interface to bounds-period semantics.

PeriodToString.convert(...) always formats "fields.boundsPeriod". Given the generic name PeriodToString, future callers may reasonably expect to convert a period for non-bounds contexts and get an unexpectedly bounds-shaped message back. Two non-blocking options:

  • Make the message key a (default-overridable) method, e.g. default String getMessageKey() { return "fields.boundsPeriod"; } and use it on line 107.
  • Or rename the interface to reflect its actual scope (e.g. BoundsPeriodToString).

Either makes the contract explicit. Defer if you intend to keep this strictly bounds-period-scoped.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/src/main/java/io/github/jy95/fds/common/functions/PeriodToString.java`
around lines 103 - 110, PeriodToString.convert currently hardcodes
"fields.boundsPeriod", coupling the generic interface to bounds semantics; add a
default method on the interface like default String getMessageKey() { return
"fields.boundsPeriod"; } and change PeriodToString.convert to call
translationService.getMessage(getMessageKey()) so callers can override
getMessageKey() (or alternatively rename the interface if you want to lock it to
bounds semantics).

22-32: 💤 Low value

Use Javadoc-style comments for the public abstract methods.

Lines 23, 25, 27, 29, 31 use plain block comments (/* ... */) for the abstract API of a public interface. The default methods directly below (lines 34-56, 58-64, 94-102) use proper Javadoc (/** ... */). For consistency and tooling/IDE/Javadoc generation, prefer Javadoc on the abstract methods too.

📝 Proposed fix
-    /* True if the period has a start date */
+    /**
+     * `@param` period The period to inspect.
+     * `@return` True if the period has a start date.
+     */
     boolean hasStart(P period);
-    /* True if the period has an end date */
+    /**
+     * `@param` period The period to inspect.
+     * `@return` True if the period has an end date.
+     */
     boolean hasEnd(P period);
-    /* Get the start date of the period */
+    /**
+     * `@param` period The period to inspect.
+     * `@return` The start date of the period.
+     */
     D getStart(P period);
-    /* Get the end date of the period */
+    /**
+     * `@param` period The period to inspect.
+     * `@return` The end date of the period.
+     */
     D getEnd(P period);
-    /* Format a date (start or end) of the period */
+    /**
+     * Format a date (start or end) of the period.
+     *
+     * `@param` translationService The service providing localized translations.
+     * `@param` date The date to format.
+     * `@return` The formatted date string.
+     */
     String formatDateTime(TranslationService<C> translationService, D date);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/src/main/java/io/github/jy95/fds/common/functions/PeriodToString.java`
around lines 22 - 32, The five public abstract methods in
PeriodToString—hasStart(P period), hasEnd(P period), getStart(P period),
getEnd(P period), and formatDateTime(TranslationService<C> translationService, D
date)—use block comments; replace each block comment with Javadoc-style comments
(/** ... */) describing the method purpose and parameters so the interface's
public API matches the existing Javadoc used by the default methods and supports
IDE/tooling Javadoc generation.

65-92: 💤 Low value

Stale comment references FormatDateTimes.convert() which is not used here.

The comment on line 70 says "Prepare date values using FormatDateTimes.convert()", but the code calls formatDateTime(translationService, ...). This appears to be a leftover from the previous implementation. Consider updating to keep documentation in sync with the code.

📝 Proposed fix
-        // Prepare date values using FormatDateTimes.convert()
+        // Prepare date values via the implementation-provided formatDateTime
         String startDate = GenericOperations.conditionalSelect(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/src/main/java/io/github/jy95/fds/common/functions/PeriodToString.java`
around lines 65 - 92, The inline comment above extractInformation incorrectly
references FormatDateTimes.convert(); update or remove it so it reflects the
actual implementation: mention that startDate and endDate are prepared via the
formatDateTime(TranslationService<C>, ...) helper called in extractInformation
(or simply remove the outdated reference), keeping the comment consistent with
the method extractInformation and the formatDateTime usage.
common/src/main/java/io/github/jy95/fds/common/translators/timing/repeat/BoundsPeriod.java (1)

29-42: 💤 Low value

Type parameter D is reused with two different meanings between BoundsPeriod and PeriodToString.

Here D represents the data/host type (e.g. TimingRepeatComponent), while in PeriodToString<P, D, C> the same letter denotes the date primitive (extends IPrimitiveType<Date>). Consider renaming this class's parameter (e.g. T for the data type) to avoid the cognitive overlap when both types are in scope at call sites.

📝 Proposed rename
-public class BoundsPeriod<
-D,
-P extends IBase,
-C extends FDSConfig
-> implements Translator<D> {
+public class BoundsPeriod<
+T,
+P extends IBase,
+C extends FDSConfig
+> implements Translator<T> {
 
     /* Translation service for handling translation logic */
     private final TranslationService<C> translationService;
     /* PeriodToString converter for converting the period to a human-readable string */
     private final PeriodToString<P, ?, C> periodToString;
     /* Predicate to check the presence of the boundsPeriod field in the data */
-    private final Predicate<D> presenceChecker;
+    private final Predicate<T> presenceChecker;
     /* Function to extract the boundsPeriod from the data */
-    private final Function<D, P> extractor;
+    private final Function<T, P> extractor;
 
     `@Override`
-    public boolean isPresent(D data) {
+    public boolean isPresent(T data) {
         return presenceChecker.test(data);
     }
 
     `@Override`
-    public CompletableFuture<String> convert(D data) {
+    public CompletableFuture<String> convert(T data) {
         var period = extractor.apply(data);
         return periodToString.convert(translationService, period);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@common/src/main/java/io/github/jy95/fds/common/translators/timing/repeat/BoundsPeriod.java`
around lines 29 - 42, The generic type D in BoundsPeriod clashes semantically
with the D used in PeriodToString<P, D, C>; rename the BoundsPeriod data type
parameter (suggestion: T) and update all usages accordingly: change class
declaration BoundsPeriod<D, P extends IBase, C extends FDSConfig> to
BoundsPeriod<T, P extends IBase, C extends FDSConfig> and update Translator<D>,
Predicate<D>, Function<D, P>, and any other methods/fields referencing D (e.g.
presenceChecker, extractor) to use T so the PeriodToString type parameter
remains the date primitive D without confusion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@common/src/main/java/io/github/jy95/fds/common/functions/PeriodToString.java`:
- Around line 103-110: PeriodToString.convert currently hardcodes
"fields.boundsPeriod", coupling the generic interface to bounds semantics; add a
default method on the interface like default String getMessageKey() { return
"fields.boundsPeriod"; } and change PeriodToString.convert to call
translationService.getMessage(getMessageKey()) so callers can override
getMessageKey() (or alternatively rename the interface if you want to lock it to
bounds semantics).
- Around line 22-32: The five public abstract methods in
PeriodToString—hasStart(P period), hasEnd(P period), getStart(P period),
getEnd(P period), and formatDateTime(TranslationService<C> translationService, D
date)—use block comments; replace each block comment with Javadoc-style comments
(/** ... */) describing the method purpose and parameters so the interface's
public API matches the existing Javadoc used by the default methods and supports
IDE/tooling Javadoc generation.
- Around line 65-92: The inline comment above extractInformation incorrectly
references FormatDateTimes.convert(); update or remove it so it reflects the
actual implementation: mention that startDate and endDate are prepared via the
formatDateTime(TranslationService<C>, ...) helper called in extractInformation
(or simply remove the outdated reference), keeping the comment consistent with
the method extractInformation and the formatDateTime usage.

In
`@common/src/main/java/io/github/jy95/fds/common/translators/timing/repeat/BoundsPeriod.java`:
- Around line 29-42: The generic type D in BoundsPeriod clashes semantically
with the D used in PeriodToString<P, D, C>; rename the BoundsPeriod data type
parameter (suggestion: T) and update all usages accordingly: change class
declaration BoundsPeriod<D, P extends IBase, C extends FDSConfig> to
BoundsPeriod<T, P extends IBase, C extends FDSConfig> and update Translator<D>,
Predicate<D>, Function<D, P>, and any other methods/fields referencing D (e.g.
presenceChecker, extractor) to use T so the PeriodToString type parameter
remains the date primitive D without confusion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 57b4ef7c-0968-4aaa-b6cf-ea3b5fa2630c

📥 Commits

Reviewing files that changed from the base of the PR and between e70dd08 and cd2707a.

📒 Files selected for processing (8)
  • common/src/main/java/io/github/jy95/fds/common/functions/PeriodToString.java
  • common/src/main/java/io/github/jy95/fds/common/translators/timing/repeat/BoundsPeriod.java
  • r4/src/main/java/io/github/jy95/fds/r4/functions/PeriodToStringR4.java
  • r4/src/main/java/io/github/jy95/fds/r4/translators/BoundsPeriodR4.java
  • r4/src/main/java/io/github/jy95/fds/r4/utils/maps/TimingRepeatTranslatorsMapR4.java
  • r5/src/main/java/io/github/jy95/fds/r5/functions/PeriodToStringR5.java
  • r5/src/main/java/io/github/jy95/fds/r5/translators/BoundsPeriodR5.java
  • r5/src/main/java/io/github/jy95/fds/r5/utils/maps/TimingRepeatTranslatorsMapR5.java
💤 Files with no reviewable changes (2)
  • r5/src/main/java/io/github/jy95/fds/r5/translators/BoundsPeriodR5.java
  • r4/src/main/java/io/github/jy95/fds/r4/translators/BoundsPeriodR4.java

@jy95 jy95 merged commit 541f46d into main May 7, 2026
20 checks passed
@jy95 jy95 deleted the PeriodToString branch May 7, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant