Skip to content

Commit 1a0ef48

Browse files
author
Dennis Labordus
committed
Clean up old HItem related to CoMPAS
Signed-off-by: Dennis Labordus <[email protected]>
1 parent e9199fe commit 1a0ef48

File tree

8 files changed

+203
-40
lines changed

8 files changed

+203
-40
lines changed

repository/src/main/java/org/lfenergy/compas/scl/data/model/Version.java

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
package org.lfenergy.compas.scl.data.model;
55

66
import org.eclipse.microprofile.openapi.annotations.media.Schema;
7-
import org.lfenergy.compas.scl.data.exception.CompasSclDataServiceException;
87

98
import java.util.Objects;
109

11-
import static org.lfenergy.compas.scl.data.exception.CompasSclDataServiceErrorCode.UNKNOWN_CHANGE_SET_TYPE_ERROR_CODE;
12-
1310
@Schema(description = "Presenting the version logic used in CoMPAS.")
14-
public class Version {
11+
public class Version implements Comparable<Version> {
12+
public static final String PATTERN = "([1-9]\\d*)\\.(\\d+)\\.(\\d+)";
13+
1514
@Schema(description = "The major version.", example = "2")
1615
private final int majorVersion;
1716
@Schema(description = "The minor version.", example = "1")
@@ -40,7 +39,7 @@ private void validate(String version) {
4039
if (version == null || version.isEmpty()) {
4140
throw new IllegalArgumentException("Version can't be null or empty");
4241
}
43-
if (!version.matches("([1-9]\\d*)\\.(\\d+)\\.(\\d+)")) {
42+
if (!version.matches(PATTERN)) {
4443
throw new IllegalArgumentException("Version is in the wrong format. Must consist of 3 number separated by dot (1.3.5)");
4544
}
4645
}
@@ -50,16 +49,11 @@ public Version getNextVersion(ChangeSetType changeSetType) {
5049
throw new IllegalArgumentException("ChangeSetType can't be null or empty");
5150
}
5251

53-
switch (changeSetType) {
54-
case MAJOR:
55-
return new Version(majorVersion + 1, 0, 0);
56-
case MINOR:
57-
return new Version(majorVersion, minorVersion + 1, 0);
58-
case PATCH:
59-
return new Version(majorVersion, minorVersion, patchVersion + 1);
60-
default:
61-
throw new CompasSclDataServiceException(UNKNOWN_CHANGE_SET_TYPE_ERROR_CODE, "Unhandled ChangeSetType " + changeSetType);
62-
}
52+
return switch (changeSetType) {
53+
case MAJOR -> new Version(majorVersion + 1, 0, 0);
54+
case MINOR -> new Version(majorVersion, minorVersion + 1, 0);
55+
case PATCH -> new Version(majorVersion, minorVersion, patchVersion + 1);
56+
};
6357
}
6458

6559
public int getMajorVersion() {
@@ -92,6 +86,17 @@ public boolean equals(final Object obj) {
9286
}
9387
}
9488

89+
@Override
90+
public int compareTo(Version otherVersion) {
91+
if (this.majorVersion == otherVersion.getMajorVersion()) {
92+
if (this.minorVersion == otherVersion.getMinorVersion()) {
93+
return Integer.compare(this.patchVersion, otherVersion.getPatchVersion());
94+
}
95+
return Integer.compare(this.minorVersion, otherVersion.getMinorVersion());
96+
}
97+
return Integer.compare(this.majorVersion, otherVersion.getMajorVersion());
98+
}
99+
95100
@Override
96101
public String toString() {
97102
return majorVersion + "." + minorVersion + "." + patchVersion;

repository/src/main/java/org/lfenergy/compas/scl/data/util/SclElementProcessor.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,50 @@ public Element addCompasElement(Element compasPrivate, String localName, String
9898
return element;
9999
}
100100

101+
/**
102+
* The method will remove all newer Hitem Element, including the version passed from the History Element.
103+
* It will search for Hitem Element where the Revision Attribute is empty and the version has the
104+
* pattern "Major version"."Minor version"."Patch version".
105+
* <p>
106+
* If the version is the same or newer the Hitem will be removed from the History Element.
107+
*
108+
* @param headerElement The Header Element containing the History Items.
109+
* @param version The version from which to remove.
110+
*/
111+
public void cleanupHistoryItem(Element headerElement, Version version) {
112+
var history = getChildNodesByName(headerElement, SCL_HISTORY_ELEMENT_NAME, SCL_NS_URI).stream().findFirst();
113+
history.ifPresent(historyElement ->
114+
getChildNodesByName(historyElement, SCL_HITEM_ELEMENT_NAME, SCL_NS_URI)
115+
.stream()
116+
.filter(hItemElement -> hItemElement.getAttribute("revision").isBlank())
117+
.forEach(hItemElement -> {
118+
if (shouldRemoveHItem(hItemElement, version)) {
119+
historyElement.removeChild(hItemElement);
120+
}
121+
})
122+
);
123+
}
124+
125+
/**
126+
* Check if the version uses the pattern that matches the one used by CoMPAS and if this is the case
127+
* compare the two versions and determine if the HItem version is smaller or the same as the new one
128+
* being created.
129+
*
130+
* @param hItemElement The HItem Element to check the version attribute from.
131+
* @param version The new version that will be created.
132+
* @return True if the HItem has a smaller or the same version and should be removed.
133+
*/
134+
boolean shouldRemoveHItem(Element hItemElement, Version version) {
135+
var hItemVersion = hItemElement.getAttribute("version");
136+
if (hItemVersion.isBlank() || !hItemVersion.matches(Version.PATTERN)) {
137+
return false;
138+
}
139+
return version.compareTo(new Version(hItemVersion)) <= 0;
140+
}
141+
101142
/**
102143
* Add a Hitem to the History Element from the Header. If the Header doesn't contain a History Element
103-
* this Element will also be created. The revision attribute will be empty, the when will be set to the
144+
* this Element will also be created. The revision attribute will be empty, the "when" will be set to the
104145
* current date.
105146
*
106147
* @param header The Header Element from SCL under which the History Element can be found/added.

repository/src/test/java/org/lfenergy/compas/scl/data/model/VersionTest.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ void constructor_WhenCalledWithNullOrEmptyValue_ThenExceptionThrown() {
1313
var expectedMessage = "Version can't be null or empty";
1414

1515
assertThrows(IllegalArgumentException.class, () -> new Version(null), expectedMessage);
16-
1716
assertThrows(IllegalArgumentException.class, () -> new Version(""), expectedMessage);
1817
}
1918

@@ -93,4 +92,25 @@ void equals_WhenTwoSameObject_ThenEqualsShouldAlsoBeTheSame() {
9392
void toString_WhenCalled_ThenCorrectStringReturned() {
9493
assertEquals("1.5.8", new Version(1, 5, 8).toString());
9594
}
95+
96+
@Test
97+
void compareTo_WhenWhenMajorVersionAreDifferent_ThenCorrectResult() {
98+
assertEquals(-1, new Version("1.0.0").compareTo(new Version("2.0.0")));
99+
assertEquals(0, new Version("1.0.0").compareTo(new Version("1.0.0")));
100+
assertEquals(1, new Version("2.0.0").compareTo(new Version("1.0.0")));
101+
}
102+
103+
@Test
104+
void compareTo_WhenWhenMinorVersionAreDifferent_ThenCorrectResult() {
105+
assertEquals(-1, new Version("1.1.0").compareTo(new Version("1.2.0")));
106+
assertEquals(0, new Version("1.1.0").compareTo(new Version("1.1.0")));
107+
assertEquals(1, new Version("1.2.0").compareTo(new Version("1.1.0")));
108+
}
109+
110+
@Test
111+
void compareTo_WhenWhenPathVersionAreDifferent_ThenCorrectResult() {
112+
assertEquals(-1, new Version("1.1.1").compareTo(new Version("1.1.2")));
113+
assertEquals(0, new Version("1.1.1").compareTo(new Version("1.1.1")));
114+
assertEquals(1, new Version("1.1.2").compareTo(new Version("1.1.1")));
115+
}
96116
}

repository/src/test/java/org/lfenergy/compas/scl/data/util/SclElementProcessorTest.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
import org.lfenergy.compas.scl.data.model.Version;
1010
import org.w3c.dom.Element;
1111

12+
import java.util.List;
13+
import java.util.Optional;
14+
1215
import static org.junit.jupiter.api.Assertions.*;
1316
import static org.lfenergy.compas.scl.data.SclDataServiceConstants.*;
1417
import static org.lfenergy.compas.scl.data.exception.CompasSclDataServiceErrorCode.HEADER_NOT_FOUND_ERROR_CODE;
@@ -188,6 +191,73 @@ void getAttributeValue_WhenCalledForNonExistingAttribute_ThenOptionalEmptyReturn
188191
assertFalse(result.isPresent());
189192
}
190193

194+
@Test
195+
void cleanupHistoryItem_WhenCalledWithVersion_ThenSameAndNewerVersionsAreRemoved() {
196+
var scl = readSCL("scl_cleanup_history.scd");
197+
198+
assertEquals(7, getHItems(scl).size());
199+
processor.cleanupHistoryItem(processor.getSclHeader(scl).orElseThrow(), new Version("1.0.2"));
200+
assertEquals(5, getHItems(scl).size());
201+
}
202+
203+
@Test
204+
void shouldRemoveHItem_WhenCalledWithInvalidVersion_ThenFalseReturned() {
205+
var scl = readSCL("scl_cleanup_history.scd");
206+
var hItem = getHItem(scl, "Siemens");
207+
208+
assertFalse(processor.shouldRemoveHItem(hItem, new Version("1.0.2")));
209+
}
210+
211+
@Test
212+
void shouldRemoveHItem_WhenCalledWithEmptyVersion_ThenFalseReturned() {
213+
var scl = readSCL("scl_cleanup_history.scd");
214+
var hItem = getHItem(scl, "Empty");
215+
216+
assertFalse(processor.shouldRemoveHItem(hItem, new Version("1.0.2")));
217+
}
218+
219+
@Test
220+
void shouldRemoveHItem_WhenCalledWithOlderVersion_ThenFalseReturned() {
221+
var scl = readSCL("scl_cleanup_history.scd");
222+
var hItem = getHItem(scl, "Created");
223+
224+
assertFalse(processor.shouldRemoveHItem(hItem, new Version("1.0.2")));
225+
}
226+
227+
@Test
228+
void shouldRemoveHItem_WhenCalledWithSameVersion_ThenTrueReturned() {
229+
var scl = readSCL("scl_cleanup_history.scd");
230+
var hItem = getHItem(scl, "Updated 1");
231+
232+
assertTrue(processor.shouldRemoveHItem(hItem, new Version("1.0.2")));
233+
}
234+
235+
@Test
236+
void shouldRemoveHItem_WhenCalledWithNewerVersion_ThenTrueReturned() {
237+
var scl = readSCL("scl_cleanup_history.scd");
238+
var hItem = getHItem(scl, "Updated 2");
239+
240+
assertTrue(processor.shouldRemoveHItem(hItem, new Version("1.0.2")));
241+
}
242+
243+
private List<Element> getHItems(Element scl) {
244+
return processor.getSclHeader(scl)
245+
.map(header -> processor.getChildNodeByName(header, SCL_HISTORY_ELEMENT_NAME, SCL_NS_URI))
246+
.filter(Optional::isPresent)
247+
.map(Optional::get)
248+
.map(history -> processor.getChildNodesByName(history, SCL_HITEM_ELEMENT_NAME, SCL_NS_URI))
249+
.stream()
250+
.flatMap(List::stream)
251+
.toList();
252+
}
253+
254+
private Element getHItem(Element scl, String what) {
255+
return getHItems(scl).stream()
256+
.filter(element -> element.getAttribute("what").equals(what))
257+
.findFirst()
258+
.get();
259+
}
260+
191261
private Element readSCL(String sclFilename) {
192262
var inputStream = getClass().getResourceAsStream("/scl/" + sclFilename);
193263
assert inputStream != null;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!-- SPDX-FileCopyrightText: 2021 Alliander N.V. -->
2+
<!-- -->
3+
<!-- SPDX-License-Identifier: Apache-2.0 -->
4+
<SCL xmlns="http://www.iec.ch/61850/2003/SCL" version="2007" revision="B" release="4">
5+
<Header id="370e5d89-df3a-4d9b-a651-3dc5cb28bee1" version="1.0.0">
6+
<History>
7+
<Hitem revision="" version="0.0.1" what="Init" when="2021-09-01T01:57:56+02:00" who="Mr Editor"/>
8+
<Hitem revision="" version="1.0.0" what="Created" when="2021-09-01T01:57:56+02:00" who="Mr Editor"/>
9+
<Hitem revision="" version="1.0.2" what="Updated 1" when="2021-09-01T01:57:56+02:00" who="Mr Editor"/>
10+
<Hitem revision="" version="2.0.0" what="Updated 2" when="2021-09-01T01:57:56+02:00" who="Mr Editor"/>
11+
<Hitem revision="" version="Siemens" what="Siemens" when="2021-09-01T01:57:56+02:00" who="Mr Editor"/>
12+
<Hitem revision="revision" version="Siemens" what="Siemens revision" when="2021-09-01T01:57:56+02:00"
13+
who="Mr Editor"/>
14+
<Hitem revision="" version="" what="Empty" when="2021-09-01T01:57:56+02:00" who="Mr Editor"/>
15+
</History>
16+
</Header>
17+
</SCL>

service/src/main/java/org/lfenergy/compas/scl/data/service/CompasSclDataService.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ public String create(SclFileType type, String name, String who, String comment,
133133

134134
// Update the Header of the SCL (or create if not exists.)
135135
var header = createOrUpdateHeader(scl, id, version);
136-
createHistoryItem(header, "SCL created", who, comment, version);
136+
sclElementProcessor.cleanupHistoryItem(header, version);
137+
sclElementProcessor.addHistoryItem(header, who, createMessage("SCL created", comment), version);
137138

138139
// Update or add the Compas Private Element to the SCL File.
139140
setSclCompasPrivateElement(scl, name, type);
@@ -149,7 +150,7 @@ public String create(SclFileType type, String name, String who, String comment,
149150
/**
150151
* Create a new version of a specific SCL XML File. The content will be the passed SCL XML File.
151152
* The UUID and new version (depending on the passed ChangeSetType) are set and
152-
* the CoMPAS Private elements will also be copied, the SCL Name will only be copied if not available in the passed
153+
* the CoMPAS Private elements will also be copied, the SCL Name will only be copied if it isn't available in the
153154
* SCL XML File.
154155
*
155156
* @param type The type to update it for.
@@ -180,7 +181,8 @@ public String update(SclFileType type, UUID id, ChangeSetType changeSetType, Str
180181

181182
// Update the Header of the SCL (or create if not exists.)
182183
var header = createOrUpdateHeader(scl, id, version);
183-
createHistoryItem(header, "SCL updated", who, comment, version);
184+
sclElementProcessor.cleanupHistoryItem(header, version);
185+
sclElementProcessor.addHistoryItem(header, who, createMessage("SCL updated", comment), version);
184186

185187
// Update or add the Compas Private Element to the SCL File.
186188
var newSclName = newFileName.orElse(currentSclMetaInfo.getName());
@@ -278,20 +280,18 @@ private void setSclCompasPrivateElement(Element scl, String name, SclFileType fi
278280
}
279281

280282
/**
281-
* Add a Hitem to the current or added History Element from the Header.
283+
* If a comment is added by the user, a standard message will be joined together with the comment from the user.
282284
*
283-
* @param header The header of SCL file to edit.
284-
* @param message The message set on the Hitem.
285-
* @param who The user that made the change.
286-
* @param comment If filled the comment that will be added to the message.
287-
* @param version The version set on the Hitem.
285+
* @param standardMessage The standard message.
286+
* @param comment The comment a user may have added.
287+
* @return The full message to be added to the HItem.
288288
*/
289-
private void createHistoryItem(Element header, String message, String who, String comment, Version version) {
290-
var fullmessage = message;
289+
private String createMessage(String standardMessage, String comment) {
290+
var message = standardMessage;
291291
if (comment != null && !comment.isBlank()) {
292-
fullmessage += ", " + comment;
292+
message += ", " + comment;
293293
}
294-
sclElementProcessor.addHistoryItem(header, who, fullmessage, version);
294+
return message;
295295
}
296296

297297
/**

service/src/test/java/org/lfenergy/compas/scl/data/service/CompasSclDataServiceTest.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ void create_WhenCalledWithOutCompasExtension_ThenSCLReturnedWithCorrectCompasExt
127127

128128
assertNotNull(scl);
129129
assertCompasExtension(scl, name);
130-
assertHistoryItem(scl, INITIAL_VERSION, comment);
130+
assertHistoryItem(scl, 2, INITIAL_VERSION, comment);
131131
verify(compasSclDataRepository, times(1)).create(eq(SCL_TYPE), any(UUID.class), eq(name), anyString(), eq(INITIAL_VERSION), eq(who), eq(emptyList()));
132132
verify(compasSclDataRepository, times(1)).hasDuplicateSclName(SCL_TYPE, name);
133133
}
@@ -148,7 +148,7 @@ void create_WhenCalledWithCompasExtension_ThenSCLReturnedWithCorrectCompasExtens
148148

149149
assertNotNull(scl);
150150
assertCompasExtension(scl, name);
151-
assertHistoryItem(scl, INITIAL_VERSION, comment);
151+
assertHistoryItem(scl, 2, INITIAL_VERSION, comment);
152152
verify(compasSclDataRepository, times(1)).create(eq(SCL_TYPE), any(UUID.class), eq(name), anyString(), eq(INITIAL_VERSION), eq(who), eq(emptyList()));
153153
verify(compasSclDataRepository, times(1)).hasDuplicateSclName(SCL_TYPE, name);
154154
}
@@ -201,7 +201,7 @@ void update_WhenCalledWithoutCompasElements_ThenSCLReturnedWithCorrectCompasExte
201201

202202
assertNotNull(scl);
203203
assertCompasExtension(scl, previousName);
204-
assertHistoryItem(scl, nextVersion, null);
204+
assertHistoryItem(scl, 4, nextVersion, null);
205205
verify(compasSclDataRepository, times(1)).create(eq(SCL_TYPE), eq(uuid), eq(previousName), anyString(), eq(nextVersion), eq(who), eq(emptyList()));
206206
verify(compasSclDataRepository, times(1)).findMetaInfoByUUID(SCL_TYPE, uuid);
207207
verify(compasSclDataRepository, never()).hasDuplicateSclName(SCL_TYPE, previousName);
@@ -228,7 +228,7 @@ void update_WhenCalledWithCompasElementsAndNewName_ThenSCLReturnedWithCorrectCom
228228

229229
assertNotNull(scl);
230230
assertCompasExtension(scl, newName);
231-
assertHistoryItem(scl, nextVersion, null);
231+
assertHistoryItem(scl, 4, nextVersion, null);
232232
verify(compasSclDataRepository, times(1)).create(eq(SCL_TYPE), eq(uuid), eq(newName), anyString(), eq(nextVersion), eq(who), eq(emptyList()));
233233
verify(compasSclDataRepository, times(1)).findMetaInfoByUUID(SCL_TYPE, uuid);
234234
verify(compasSclDataRepository, times(1)).hasDuplicateSclName(SCL_TYPE, newName);
@@ -275,7 +275,7 @@ void update_WhenCalledWithCompasElementsAndSameName_ThenSCLReturnedWithCorrectCo
275275

276276
assertNotNull(scl);
277277
assertCompasExtension(scl, previousName);
278-
assertHistoryItem(scl, nextVersion, null);
278+
assertHistoryItem(scl, 4, nextVersion, null);
279279
verify(compasSclDataRepository, times(1)).create(eq(SCL_TYPE), eq(uuid), eq(previousName), anyString(), eq(nextVersion), eq(who), eq(emptyList()));
280280
verify(compasSclDataRepository, times(1)).findMetaInfoByUUID(SCL_TYPE, uuid);
281281
verify(compasSclDataRepository, never()).hasDuplicateSclName(SCL_TYPE, previousName);
@@ -386,7 +386,7 @@ private void assertCompasExtension(String sclData, String name) {
386386
assertEquals(SCL_TYPE.toString(), typeElement.get().getTextContent());
387387
}
388388

389-
private void assertHistoryItem(String sclData, Version version, String comment) {
389+
private void assertHistoryItem(String sclData, int expectedHItems, Version version, String comment) {
390390
var scl = converter.convertToElement(sclData, SCL_ELEMENT_NAME, SCL_NS_URI);
391391
var header = processor.getSclHeader(scl);
392392
assertTrue(header.isPresent());
@@ -395,7 +395,7 @@ private void assertHistoryItem(String sclData, Version version, String comment)
395395
assertTrue(history.isPresent());
396396

397397
var items = processor.getChildNodesByName(history.get(), SCL_HITEM_ELEMENT_NAME, SCL_NS_URI);
398-
assertFalse(items.isEmpty());
398+
assertEquals(expectedHItems, items.size());
399399
// The last item should be the one added.
400400
var item = items.get(items.size() - 1);
401401
assertEquals(version.toString(), item.getAttribute(SCL_VERSION_ATTR));

0 commit comments

Comments
 (0)