Skip to content

merge limits for revert modification#178

Open
EtienneLt wants to merge 10 commits intomainfrom
merge-limits-for-revert-modification
Open

merge limits for revert modification#178
EtienneLt wants to merge 10 commits intomainfrom
merge-limits-for-revert-modification

Conversation

@EtienneLt
Copy link
Copy Markdown
Contributor

PR Summary

for DeleteVoltageLevelOnLine and DeleteAttachingLine

  • copy limits group dans selected groups from two lines
  • merge the groups by keeping only those present on both lines
  • keep the smallest permanent limit
  • for each temporary limit if they are present in both lines with same acceptable duration it is kept with the smallest value otherwise it is removed

Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
@EtienneLt EtienneLt self-assigned this Apr 3, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Captures operational-limits context from both source lines before revert/merge operations, applies the revert modification, then computes and applies merged OperationalLimitsGroup instances and selected-group decisions to the replacing line, emitting detailed merge/delete messages into ReportNode. (≤50 words)

Changes

Cohort / File(s) Summary
Operational Limits Merging Utility
src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java
New final utility with public static methods: applyRevertModificationWithMergingOfLimits(...), setMergedOperationalLimitsGroups(...), and createMergedOperationalLimitsGroups(...). Captures selected group IDs and groups from both source lines, applies the provided revert modification, then reconciles and applies merged OperationalLimitsGroup entries (permanent limits=min, temporary limits merged only when durations match), with detailed ReportNode messages for deletions and mismatches.
Modifications Updated to Use Utility
src/main/java/org/gridsuite/modification/modifications/DeleteAttachingLine.java, src/main/java/org/gridsuite/modification/modifications/DeleteVoltageLevelOnLine.java
Replaces direct calls to revert algorithms (RevertCreateLineOnLine.apply / RevertConnectVoltageLevelOnLine.apply) with ModificationLimitsUtils.applyRevertModificationWithMergingOfLimits(...), passing original line IDs, replacing line ID, the revert algorithm instance, and the sub-report to ensure merging/reversion of operational limits is performed around the revert operation.
Reports / Message Templates
src/main/resources/org/gridsuite/modification/reports.properties
Adjusted two existing templates to change arrow notation (->-}) and added four new merge-related keys: network.modification.mergeLimits, network.modification.limitGroupsDeletedAfterMerge, network.modification.temporaryLimitsDeletedAfterMerge, and network.modification.propertyOfLimitsGroupDeletedAfterMerge.
Tests: New Merging Tests & Updates
src/test/java/org/gridsuite/modification/utils/MergingLimitsTest.java, src/test/java/org/gridsuite/modification/modifications/DeleteAttachingLineTest.java, src/test/java/org/gridsuite/modification/modifications/DeleteVoltageLevelOnLineTest.java
Added MergingLimitsTest with testModificationMergedLimits(...); updated existing modification tests to assert merged operational-limits counts and specific group presence; added testMergedLimits() tests and removed throws Exception from two test methods.
Test Utilities & Resources
src/test/java/org/gridsuite/modification/utils/TestUtils.java, src/test/resources/reportNode/delete-attaching-line-report.txt, src/test/resources/reportNode/delete-voltagelevel-on-line-report.txt
Added testReportNode(ReportNode,String) with line-ending normalization and explicit imports; added expected report files documenting merge outcomes, deleted/mismatched groups, and temporary-limit deletions.
Build/Test JVM Args
pom.xml
Updated maven-surefire-plugin configuration to append -Duser.language=en -Duser.country=US and -XX:+EnableDynamicAgentLoading to test JVM argLine.

Sequence Diagram(s)

sequenceDiagram
    participant Client as DeleteAttachingLine / DeleteVoltageLevelOnLine
    participant Network as Network
    participant ModUtils as ModificationLimitsUtils
    participant Reverter as RevertCreate/ConnectOnLine
    participant Report as ReportNode

    Client->>Network: read source lines (line1, line2) and their operational limits
    Client->>ModUtils: call applyRevertModificationWithMergingOfLimits(line1Id,line2Id,replacingId,algo,subReport)
    ModUtils->>Network: snapshot selectedOperationalLimitsGroupIds and groups for both sides
    ModUtils->>Reverter: invoke provided revert algorithm (algo.apply)
    Reverter->>Network: perform revert/merge and produce replacingLine
    Reverter-->>ModUtils: return control
    ModUtils->>Network: clear existing groups on replacingLine
    ModUtils->>ModUtils: createMergedOperationalLimitsGroups(side1) — intersect IDs, merge CurrentLimits, reconcile properties, log deletions/mismatches
    ModUtils->>ModUtils: createMergedOperationalLimitsGroups(side2)
    ModUtils->>Network: attach merged groups and set selected group IDs on replacingLine
    ModUtils->>Report: emit merge summary and detailed deletion/property-difference entries
    Client->>Report: finalize modification report
Loading
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: implementing logic to merge operational limits when reverting modifications in DeleteVoltageLevelOnLine and DeleteAttachingLine operations.
Description check ✅ Passed The description is directly related to the changeset, providing specific details about the limits merging behavior including how permanent and temporary limits are handled.

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


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.

Copy link
Copy Markdown

@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.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java (2)

40-41: Potential ConcurrentModificationException when removing while iterating.

The forEach creates an iterator over the collection while removeOperationalLimitsGroup modifies the underlying collection. This may cause a ConcurrentModificationException depending on the collection implementation.

♻️ Safer approach: collect IDs first, then remove
         // remove all limitsGroups as the modification does not do as wanted
-        mergedLine.getOperationalLimitsGroups1().forEach(group -> mergedLine.removeOperationalLimitsGroup1(group.getId()));
-        mergedLine.getOperationalLimitsGroups2().forEach(group -> mergedLine.removeOperationalLimitsGroup2(group.getId()));
+        List<String> groupIds1 = mergedLine.getOperationalLimitsGroups1().stream()
+                .map(OperationalLimitsGroup::getId).toList();
+        groupIds1.forEach(mergedLine::removeOperationalLimitsGroup1);
+        List<String> groupIds2 = mergedLine.getOperationalLimitsGroups2().stream()
+                .map(OperationalLimitsGroup::getId).toList();
+        groupIds2.forEach(mergedLine::removeOperationalLimitsGroup2);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`
around lines 40 - 41, The current removal loops iterate
mergedLine.getOperationalLimitsGroups1() and getOperationalLimitsGroups2() while
calling mergedLine.removeOperationalLimitsGroup1(...) /
removeOperationalLimitsGroup2(...), which can cause
ConcurrentModificationException; fix by first collecting the group IDs into a
separate List (e.g., via stream().map(Group::getId).collect(toList())) for each
of getOperationalLimitsGroups1() and getOperationalLimitsGroups2(), then iterate
those ID lists and call removeOperationalLimitsGroup1(id) and
removeOperationalLimitsGroup2(id) respectively so removals do not mutate the
collection being iterated.

52-57: Selected group not set when both lines have the same group but only one has it selected.

Per PR description, limits should be merged when present on both lines. However, if selectedGroupLine1Side1 is null but selectedGroupLine2Side1 is not (or vice versa), the selected group won't be set even though both lines have the group. Consider setting the selected group if the group exists and at least one line has it selected.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`
around lines 52 - 57, The current checks only set
mergedLine.setSelectedOperationalLimitsGroup1/Group2 when
selectedGroupLine1Side1/Side2 is non-null and equals the other line's selected
value, so if one line has the group selected and the other doesn't the merged
selection is skipped; change the logic to pick the non-null selected group when
either selectedGroupLine1Side1 or selectedGroupLine2Side1 is non-null (and
likewise for Side2) but only after verifying the group exists on both source
lines — e.g., compute a selected = selectedGroupLine1Side1 != null ?
selectedGroupLine1Side1 : selectedGroupLine2Side1 and set
mergedLine.setSelectedOperationalLimitsGroup1(selected) when selected != null
and the same group is present on the other line (use the existing group-presence
flags/variables for the two lines); apply the same change for
setSelectedOperationalLimitsGroup2 using
selectedGroupLine1Side2/selectedGroupLine2Side2.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`:
- Around line 174-185: The loop over temporaryLimitsOnLine2.keySet() uses the
wrong map in the existence check causing items only in line2 to never be treated
as deleted; change the condition to check
!temporaryLimitsOnLine1.containsKey(temporaryLimitName) so that when a key from
temporaryLimitsOnLine2 is absent in temporaryLimitsOnLine1 you call
logDeletedTemporaryLimits with the removedTemporaryLimit
(temporaryLimitsOnLine2.get(temporaryLimitName)); keep the existing
duration-comparison branch that compares temporaryLimitLine1 and
temporaryLimitLine2 using temporaryLimitsOnLine1.get(...) and
temporaryLimitsOnLine2.get(...).

In `@src/main/resources/org/gridsuite/modification/reports.properties`:
- Line 239: The message value for the key network.modification.propertyChanged
contains an accidental character sequence "-}" between ${from} and ${to}; update
the string to restore the intended arrow "->" so it reads "... ${from} -> ${to}"
and remove the stray brace. Also search for the same typo in the other affected
key mentioned (around the duplicate at line 309) and make the identical
correction in that property entry.

In `@src/test/java/org/gridsuite/modification/utils/TestUtils.java`:
- Around line 175-188: In TestUtils.testReportNode, the InputStream refStream
obtained via TestUtils.class.getResourceAsStream(reportsFile) is not closed;
wrap the resource use in a try-with-resources (e.g., try (InputStream refStream
= TestUtils.class.getResourceAsStream(reportsFile)) { ... }) so the stream is
automatically closed after reading, keep the assertNotNull check inside the try
(or handle a null by failing early), and compute expected using
ByteStreams.toByteArray(refStream) within that try block before comparing
normalized strings.

---

Nitpick comments:
In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`:
- Around line 40-41: The current removal loops iterate
mergedLine.getOperationalLimitsGroups1() and getOperationalLimitsGroups2() while
calling mergedLine.removeOperationalLimitsGroup1(...) /
removeOperationalLimitsGroup2(...), which can cause
ConcurrentModificationException; fix by first collecting the group IDs into a
separate List (e.g., via stream().map(Group::getId).collect(toList())) for each
of getOperationalLimitsGroups1() and getOperationalLimitsGroups2(), then iterate
those ID lists and call removeOperationalLimitsGroup1(id) and
removeOperationalLimitsGroup2(id) respectively so removals do not mutate the
collection being iterated.
- Around line 52-57: The current checks only set
mergedLine.setSelectedOperationalLimitsGroup1/Group2 when
selectedGroupLine1Side1/Side2 is non-null and equals the other line's selected
value, so if one line has the group selected and the other doesn't the merged
selection is skipped; change the logic to pick the non-null selected group when
either selectedGroupLine1Side1 or selectedGroupLine2Side1 is non-null (and
likewise for Side2) but only after verifying the group exists on both source
lines — e.g., compute a selected = selectedGroupLine1Side1 != null ?
selectedGroupLine1Side1 : selectedGroupLine2Side1 and set
mergedLine.setSelectedOperationalLimitsGroup1(selected) when selected != null
and the same group is present on the other line (use the existing group-presence
flags/variables for the two lines); apply the same change for
setSelectedOperationalLimitsGroup2 using
selectedGroupLine1Side2/selectedGroupLine2Side2.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 567defb6-36dc-4b54-9dd9-440119e206aa

📥 Commits

Reviewing files that changed from the base of the PR and between ec20f5e and a0e10e0.

📒 Files selected for processing (10)
  • src/main/java/org/gridsuite/modification/modifications/DeleteAttachingLine.java
  • src/main/java/org/gridsuite/modification/modifications/DeleteVoltageLevelOnLine.java
  • src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java
  • src/main/resources/org/gridsuite/modification/reports.properties
  • src/test/java/org/gridsuite/modification/modifications/DeleteAttachingLineTest.java
  • src/test/java/org/gridsuite/modification/modifications/DeleteVoltageLevelOnLineTest.java
  • src/test/java/org/gridsuite/modification/utils/MergingLimitsTest.java
  • src/test/java/org/gridsuite/modification/utils/TestUtils.java
  • src/test/resources/reportNode/delete-attaching-line-report.txt
  • src/test/resources/reportNode/delete-voltagelevel-on-line-report.txt

Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
Copy link
Copy Markdown

@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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/test/java/org/gridsuite/modification/utils/MergingLimitsTest.java (1)

84-89: Document precondition: property1 must exist on both source lines' groups.

The test asserts property1 exists with value "value1" (lines 86-87), but this property is not set anywhere in this method. The calling test must ensure the network has property1 already configured on both l1 and l2's group1 with identical values, otherwise this assertion will fail unexpectedly.

Consider adding a brief comment or setup assertion to make this precondition explicit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/gridsuite/modification/utils/MergingLimitsTest.java` around
lines 84 - 89, The test MergingLimitsTest currently asserts
group1.get().getProperty("property1") equals "value1" but never ensures that
property1 was set on the source groups (the groups for l1 and l2), which can
make the assertion flaky; update the test setup to explicitly set or assert the
precondition that property1 exists with the value "value1" on both sources
before proceeding (e.g., set property1 on the groups used by l1 and l2 or add an
explicit assert that group1 for each source contains property1 == "value1"),
referencing the existing group1/getProperty calls to locate where to add the
setup/precondition check.
pom.xml (1)

58-61: Consider using user.country instead of user.region.

The system property user.region is deprecated. For setting the locale, use -Duser.country=US instead, which is the standard property for locale configuration.

Suggested fix
-                        <argLine>@{argLine} -Duser.language=en -Duser.region=US -XX:+EnableDynamicAgentLoading</argLine>
+                        <argLine>@{argLine} -Duser.language=en -Duser.country=US -XX:+EnableDynamicAgentLoading</argLine>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pom.xml` around lines 58 - 61, Update the Maven surefire/failsafe argLine
configuration to use the standard locale system property: replace
"-Duser.region=US" with "-Duser.country=US" inside the <configuration> argLine
entry so the default locale flags (the argLine that currently includes
"-Duser.language=en -Duser.region=US -XX:+EnableDynamicAgentLoading") use the
non-deprecated property.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/test/java/org/gridsuite/modification/utils/MergingLimitsTest.java`:
- Around line 78-82: In ModificationLimitsUtils.mergedCurrentLimits there is a
logic bug: the loop that intends to log temporary limits present on line2 but
not on line1 checks temporaryLimitsOnLine2.containsKey(temporaryLimitName)
(using the same map being iterated) making the branch unreachable; change the
condition to check temporaryLimitsOnLine1 (i.e., if
(!temporaryLimitsOnLine1.containsKey(temporaryLimitName))) so the method
correctly logs limits that exist on line2 but were deleted from or missing on
line1, and run/adjust unit tests that exercise the deleted-on-line2 scenario.

---

Nitpick comments:
In `@pom.xml`:
- Around line 58-61: Update the Maven surefire/failsafe argLine configuration to
use the standard locale system property: replace "-Duser.region=US" with
"-Duser.country=US" inside the <configuration> argLine entry so the default
locale flags (the argLine that currently includes "-Duser.language=en
-Duser.region=US -XX:+EnableDynamicAgentLoading") use the non-deprecated
property.

In `@src/test/java/org/gridsuite/modification/utils/MergingLimitsTest.java`:
- Around line 84-89: The test MergingLimitsTest currently asserts
group1.get().getProperty("property1") equals "value1" but never ensures that
property1 was set on the source groups (the groups for l1 and l2), which can
make the assertion flaky; update the test setup to explicitly set or assert the
precondition that property1 exists with the value "value1" on both sources
before proceeding (e.g., set property1 on the groups used by l1 and l2 or add an
explicit assert that group1 for each source contains property1 == "value1"),
referencing the existing group1/getProperty calls to locate where to add the
setup/precondition check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 960f00ae-4d33-4f5a-89a7-8ccce47132a3

📥 Commits

Reviewing files that changed from the base of the PR and between a0e10e0 and a992c52.

📒 Files selected for processing (4)
  • pom.xml
  • src/test/java/org/gridsuite/modification/utils/MergingLimitsTest.java
  • src/test/resources/reportNode/delete-attaching-line-report.txt
  • src/test/resources/reportNode/delete-voltagelevel-on-line-report.txt
✅ Files skipped from review due to trivial changes (2)
  • src/test/resources/reportNode/delete-voltagelevel-on-line-report.txt
  • src/test/resources/reportNode/delete-attaching-line-report.txt

Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
Copy link
Copy Markdown

@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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java (1)

179-191: ⚠️ Potential issue | 🔴 Critical

Critical bug: Wrong map checked causes temporary limits from line2 to never be logged as deleted, plus potential NPE.

This issue was previously flagged and remains unfixed. Line 181 checks !temporaryLimitsOnLine2.containsKey(temporaryLimitName) while iterating over temporaryLimitsOnLine2.keySet() — this condition is always false.

Additionally, because the else branch at line 184 is always taken, temporaryLimitsOnLine1.get(temporaryLimitName) on line 185 returns null when the key exists only in line2, causing a NullPointerException on line 187.

🐛 Proposed fix
         // logs for limit set deleted on line2
         for (String temporaryLimitName : temporaryLimitsOnLine2.keySet()) {
-            if (!temporaryLimitsOnLine2.containsKey(temporaryLimitName)) {
+            if (!temporaryLimitsOnLine1.containsKey(temporaryLimitName)) {
                 LoadingLimits.TemporaryLimit removedTemporaryLimit = temporaryLimitsOnLine2.get(temporaryLimitName);
                 logDeletedTemporaryLimits(reportNode, removedTemporaryLimit, operationalLimitsGroupId, line2Id, line1Id, newLineId);
-            } else {
-                LoadingLimits.TemporaryLimit temporaryLimitLine1 = temporaryLimitsOnLine1.get(temporaryLimitName);
-                LoadingLimits.TemporaryLimit temporaryLimitLine2 = temporaryLimitsOnLine2.get(temporaryLimitName);
-                if (temporaryLimitLine1.getAcceptableDuration() != temporaryLimitLine2.getAcceptableDuration()) {
-                    logDeletedTemporaryLimits(reportNode, temporaryLimitLine2, operationalLimitsGroupId, line2Id, line1Id, newLineId);
-                }
             }
+            // Note: Duration mismatch logging is already handled in the first loop (lines 167-168)
         }
         adder.add();
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`
around lines 179 - 191, The loop over temporaryLimitsOnLine2 incorrectly checks
temporaryLimitsOnLine2.containsKey(...) causing the deleted-from-line2 case to
never fire and a potential NPE when temporaryLimitsOnLine1.get(...) returns
null; change the existence check to if
(!temporaryLimitsOnLine1.containsKey(temporaryLimitName)) so you log the removed
limit from temporaryLimitsOnLine2 (use LoadingLimits.TemporaryLimit
removedTemporaryLimit = temporaryLimitsOnLine2.get(temporaryLimitName) and call
logDeletedTemporaryLimits(...)), and keep the else branch to retrieve both
temporaryLimitLine1 and temporaryLimitLine2 and compare getAcceptableDuration();
this ensures you only call temporaryLimitsOnLine1.get(...) when the key exists
and avoids the NPE.
🧹 Nitpick comments (2)
src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java (2)

195-206: Missing severity specification in report node.

Unlike other logging methods in this class (lines 40, 127, 143), this method does not call withSeverity() before add(). This inconsistency may result in a different default severity for deleted temporary limits messages.

🔧 Proposed fix: add severity for consistency
         reportNode.newReportNode()
                 .withMessageTemplate("network.modification.temporaryLimitsDeletedAfterMerge")
                 .withUntypedValue("limit_name", temporaryLimit.getName())
                 .withUntypedValue("tempo", temporaryLimit.getAcceptableDuration())
                 .withUntypedValue("limitset_name", operationalLimitsGroupName)
                 .withUntypedValue("line_with_limit", lineWithTemporaryLimit)
                 .withUntypedValue("line_without_limit", lineWithoutTemporaryLimit)
                 .withUntypedValue("replacing_line", newLineId)
+                .withSeverity(TypedValue.INFO_SEVERITY)
                 .add();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`
around lines 195 - 206, The logDeletedTemporaryLimits method is missing a
severity on the ReportNode chain, causing inconsistency with other methods;
update the report chain in logDeletedTemporaryLimits to call withSeverity(...)
(use the same severity used by the other methods in this class, e.g., the
severity passed in lines like the methods around withSeverity(...) calls) before
calling add() so the deleted temporary limits message uses the same severity
level as the other report entries.

43-45: Consider collecting IDs before removing to improve code clarity.

While the current iteration-removal pattern works correctly, collecting group IDs before removal is safer and more explicit:

Suggested refactoring
         // remove all limitsGroups as the modification does not do as wanted
-        mergedLine.getOperationalLimitsGroups1().forEach(group -> mergedLine.removeOperationalLimitsGroup1(group.getId()));
-        mergedLine.getOperationalLimitsGroups2().forEach(group -> mergedLine.removeOperationalLimitsGroup2(group.getId()));
+        List<String> groupIds1 = mergedLine.getOperationalLimitsGroups1().stream()
+                .map(OperationalLimitsGroup::getId).toList();
+        groupIds1.forEach(mergedLine::removeOperationalLimitsGroup1);
+        List<String> groupIds2 = mergedLine.getOperationalLimitsGroups2().stream()
+                .map(OperationalLimitsGroup::getId).toList();
+        groupIds2.forEach(mergedLine::removeOperationalLimitsGroup2);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`
around lines 43 - 45, The current code iterates over
mergedLine.getOperationalLimitsGroups1() and getOperationalLimitsGroups2() while
removing from mergedLine, which can be clearer and safer by first collecting the
group IDs and then removing by ID; change the blocks using
mergedLine.getOperationalLimitsGroups1().forEach(group ->
mergedLine.removeOperationalLimitsGroup1(group.getId())) and
mergedLine.getOperationalLimitsGroups2().forEach(group ->
mergedLine.removeOperationalLimitsGroup2(group.getId())) to first build lists of
IDs (e.g., List<String> ids1 =
mergedLine.getOperationalLimitsGroups1().stream().map(Group::getId).collect(...))
and then iterate over those ID lists to call
mergedLine.removeOperationalLimitsGroup1(id) and
mergedLine.removeOperationalLimitsGroup2(id).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`:
- Around line 111-130: The current merge loop only iterates
groupOnLine1.getPropertyNames(), so properties that exist only on groupOnLine2
(or only on groupOnLine1) are silently dropped; change the logic to iterate over
the union of property names from groupOnLine1 and groupOnLine2 (e.g., collect
both property name sets), and for each propertyName: fetch valueLine1 via
groupOnLine1.getProperty(propertyName) and valueLine2 via
groupOnLine2.getProperty(propertyName), set newGroup.setProperty when both
non-null and equal, and otherwise create a report via reportNode.newReportNode()
using the same "network.modification.propertyOfLimitsGroupDeletedAfterMerge"
template but correctly populate property_value_A/B and line_A/B for cases where
one side is null (unique-to-one-line) as well as differing values; use the same
TypedValue.INFO_SEVERITY and replacing_line/newGroup.getId() fields to keep
behavior consistent.

---

Duplicate comments:
In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`:
- Around line 179-191: The loop over temporaryLimitsOnLine2 incorrectly checks
temporaryLimitsOnLine2.containsKey(...) causing the deleted-from-line2 case to
never fire and a potential NPE when temporaryLimitsOnLine1.get(...) returns
null; change the existence check to if
(!temporaryLimitsOnLine1.containsKey(temporaryLimitName)) so you log the removed
limit from temporaryLimitsOnLine2 (use LoadingLimits.TemporaryLimit
removedTemporaryLimit = temporaryLimitsOnLine2.get(temporaryLimitName) and call
logDeletedTemporaryLimits(...)), and keep the else branch to retrieve both
temporaryLimitLine1 and temporaryLimitLine2 and compare getAcceptableDuration();
this ensures you only call temporaryLimitsOnLine1.get(...) when the key exists
and avoids the NPE.

---

Nitpick comments:
In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`:
- Around line 195-206: The logDeletedTemporaryLimits method is missing a
severity on the ReportNode chain, causing inconsistency with other methods;
update the report chain in logDeletedTemporaryLimits to call withSeverity(...)
(use the same severity used by the other methods in this class, e.g., the
severity passed in lines like the methods around withSeverity(...) calls) before
calling add() so the deleted temporary limits message uses the same severity
level as the other report entries.
- Around line 43-45: The current code iterates over
mergedLine.getOperationalLimitsGroups1() and getOperationalLimitsGroups2() while
removing from mergedLine, which can be clearer and safer by first collecting the
group IDs and then removing by ID; change the blocks using
mergedLine.getOperationalLimitsGroups1().forEach(group ->
mergedLine.removeOperationalLimitsGroup1(group.getId())) and
mergedLine.getOperationalLimitsGroups2().forEach(group ->
mergedLine.removeOperationalLimitsGroup2(group.getId())) to first build lists of
IDs (e.g., List<String> ids1 =
mergedLine.getOperationalLimitsGroups1().stream().map(Group::getId).collect(...))
and then iterate over those ID lists to call
mergedLine.removeOperationalLimitsGroup1(id) and
mergedLine.removeOperationalLimitsGroup2(id).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c31af441-bef2-43dd-bdcf-5062c9299ad9

📥 Commits

Reviewing files that changed from the base of the PR and between a992c52 and 38b6da3.

📒 Files selected for processing (3)
  • pom.xml
  • src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java
  • src/test/java/org/gridsuite/modification/utils/MergingLimitsTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/org/gridsuite/modification/utils/MergingLimitsTest.java

Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
Copy link
Copy Markdown

@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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java (1)

81-83: Consider defensive copying when removing elements during iteration for safer code.

While the current forEach pattern may work because getOperationalLimitsGroups1() likely returns an unmodifiable collection view, the proposed fix provides better defensive programming. The safer approach would collect group IDs first before removal to eliminate any ambiguity about concurrent modification behavior:

Proposed improvement for defensive coding
        // remove all limitsGroups as the modification does not do as wanted
-        mergedLine.getOperationalLimitsGroups1().forEach(group -> mergedLine.removeOperationalLimitsGroup1(group.getId()));
-        mergedLine.getOperationalLimitsGroups2().forEach(group -> mergedLine.removeOperationalLimitsGroup2(group.getId()));
+        mergedLine.getOperationalLimitsGroups1().stream()
+                .map(OperationalLimitsGroup::getId)
+                .toList()
+                .forEach(mergedLine::removeOperationalLimitsGroup1);
+        
+        mergedLine.getOperationalLimitsGroups2().stream()
+                .map(OperationalLimitsGroup::getId)
+                .toList()
+                .forEach(mergedLine::removeOperationalLimitsGroup2);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`
around lines 81 - 83, Collect the IDs of the groups before mutating the
collections to avoid potential concurrent-modification issues: iterate
mergedLine.getOperationalLimitsGroups1() and
mergedLine.getOperationalLimitsGroups2() to build lists of group IDs, then call
mergedLine.removeOperationalLimitsGroup1(id) and
mergedLine.removeOperationalLimitsGroup2(id) using those ID lists instead of
removing inside the forEach over the original collections.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`:
- Around line 120-125: When only one of the two Optional<CurrentLimits>
(currentLimitsLine1 or currentLimitsLine2) is present the code currently drops
the limits; update the logic so that: if both are present call
mergedCurrentLimits(currentLimitsLine1.get(), currentLimitsLine2.get(),
newGroup.newCurrentLimits(), operationalLimitGroupIdLine1, line1Id, line2Id,
newLineId, reportNode) as now, but if only one is present copy that
CurrentLimits into newGroup.newCurrentLimits() (preserve the same fields/ids
that mergedCurrentLimits would set) and emit a report/log entry using the same
reporting pattern used for deleted groups (use reportNode or the existing
logger) including operationalLimitGroupIdLine1, line1Id/line2Id and newLineId
for traceability.
- Around line 233-244: The report node created in logDeletedTemporaryLimits
(ModificationLimitsUtils.logDeletedTemporaryLimits) is missing a severity level;
update the ReportNode builder chain used in logDeletedTemporaryLimits to call
.withSeverity(TypedValue.INFO_SEVERITY) (matching other methods like
logDeletedGroups) just before the final .add() so the created report entry has
the correct INFO severity.

---

Nitpick comments:
In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`:
- Around line 81-83: Collect the IDs of the groups before mutating the
collections to avoid potential concurrent-modification issues: iterate
mergedLine.getOperationalLimitsGroups1() and
mergedLine.getOperationalLimitsGroups2() to build lists of group IDs, then call
mergedLine.removeOperationalLimitsGroup1(id) and
mergedLine.removeOperationalLimitsGroup2(id) using those ID lists instead of
removing inside the forEach over the original collections.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 110b5c15-da13-4cdc-8aa7-992f55d071aa

📥 Commits

Reviewing files that changed from the base of the PR and between 38b6da3 and 7a82656.

📒 Files selected for processing (3)
  • src/main/java/org/gridsuite/modification/modifications/DeleteAttachingLine.java
  • src/main/java/org/gridsuite/modification/modifications/DeleteVoltageLevelOnLine.java
  • src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/org/gridsuite/modification/modifications/DeleteAttachingLine.java

Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
Copy link
Copy Markdown

@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.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java (2)

227-238: ⚠️ Potential issue | 🔴 Critical

Critical bug: condition always evaluates to false making the branch unreachable.

The loop iterates over temporaryLimitsOnLine2.keySet() but checks !temporaryLimitsOnLine2.containsKey(temporaryLimitName). This condition will always be false, so temporary limits existing only on line2 will never be logged as deleted.

🐛 Proposed fix
         // logs for limit set deleted on line2
         for (String temporaryLimitName : temporaryLimitsOnLine2.keySet()) {
-            if (!temporaryLimitsOnLine2.containsKey(temporaryLimitName)) {
+            if (!temporaryLimitsOnLine1.containsKey(temporaryLimitName)) {
                 LoadingLimits.TemporaryLimit removedTemporaryLimit = temporaryLimitsOnLine2.get(temporaryLimitName);
                 logDeletedTemporaryLimits(reportNode, removedTemporaryLimit, operationalLimitsGroupId, line2Id, line1Id, newLineId);
             } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`
around lines 227 - 238, In the loop in ModificationLimitsUtils (iterating
temporaryLimitsOnLine2.keySet()), the if condition mistakenly checks
temporaryLimitsOnLine2.containsKey(temporaryLimitName) which is always true;
change it to check temporaryLimitsOnLine1.containsKey(temporaryLimitName) so
limits present in line2 but missing in line1 get logged as deleted by
logDeletedTemporaryLimits; update any null-safety if temporaryLimitsOnLine1 can
be null before calling containsKey and keep using
temporaryLimitsOnLine2.get(temporaryLimitName) to fetch the
removedTemporaryLimit.

244-252: ⚠️ Potential issue | 🟡 Minor

Missing severity on report node creates inconsistency with other logging methods.

Unlike logDeletedGroups (line 189) and property merge logging (line 164), this method does not set severity, which may affect report filtering.

🔧 Proposed fix
         reportNode.newReportNode()
                 .withMessageTemplate("network.modification.temporaryLimitsDeletedAfterMerge")
                 .withUntypedValue("limit_name", temporaryLimit.getName())
                 .withUntypedValue("tempo", temporaryLimit.getAcceptableDuration())
                 .withUntypedValue("limitset_name", operationalLimitsGroupName)
                 .withUntypedValue("line_with_limit", lineWithTemporaryLimit)
                 .withUntypedValue("line_without_limit", lineWithoutTemporaryLimit)
                 .withUntypedValue("replacing_line", newLineId)
+                .withSeverity(TypedValue.INFO_SEVERITY)
                 .add();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`
around lines 244 - 252, The report node created by
reportNode.newReportNode().withMessageTemplate(...) is missing a severity
setting, causing inconsistency with other logs; update this chain to call
.withSeverity(...) with the same severity used by logDeletedGroups and the
property-merge logging (e.g., the same enum value those methods use) so the
temporaryLimitsDeletedAfterMerge entry uses the same severity level and add any
needed import for the severity enum.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`:
- Around line 82-83: The loop removes items from the same collection view
returned by
mergedLine.getOperationalLimitsGroups1()/getOperationalLimitsGroups2(), risking
ConcurrentModificationException; fix by first collecting the group IDs into an
independent list (e.g., List<String> ids1 =
mergedLine.getOperationalLimitsGroups1().stream().map(g ->
g.getId()).collect(...)) and then iterating that list to call
mergedLine.removeOperationalLimitsGroup1(id) (do the same for
getOperationalLimitsGroups2()/removeOperationalLimitsGroup2); update the code
around mergedLine usage to avoid mutating the collection while iterating.

---

Duplicate comments:
In `@src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java`:
- Around line 227-238: In the loop in ModificationLimitsUtils (iterating
temporaryLimitsOnLine2.keySet()), the if condition mistakenly checks
temporaryLimitsOnLine2.containsKey(temporaryLimitName) which is always true;
change it to check temporaryLimitsOnLine1.containsKey(temporaryLimitName) so
limits present in line2 but missing in line1 get logged as deleted by
logDeletedTemporaryLimits; update any null-safety if temporaryLimitsOnLine1 can
be null before calling containsKey and keep using
temporaryLimitsOnLine2.get(temporaryLimitName) to fetch the
removedTemporaryLimit.
- Around line 244-252: The report node created by
reportNode.newReportNode().withMessageTemplate(...) is missing a severity
setting, causing inconsistency with other logs; update this chain to call
.withSeverity(...) with the same severity used by logDeletedGroups and the
property-merge logging (e.g., the same enum value those methods use) so the
temporaryLimitsDeletedAfterMerge entry uses the same severity level and add any
needed import for the severity enum.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 114e6391-85fc-428e-9d68-95b9ab3aa8bb

📥 Commits

Reviewing files that changed from the base of the PR and between 7a82656 and ce64bc3.

📒 Files selected for processing (2)
  • src/main/java/org/gridsuite/modification/utils/ModificationLimitsUtils.java
  • src/test/java/org/gridsuite/modification/utils/MergingLimitsTest.java

Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 7, 2026

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