Skip to content

Commit

Permalink
Merge pull request #15 from com-pas/improve-cleanup-old-coordinates
Browse files Browse the repository at this point in the history
Refactor cleanup and support multiple substations.
  • Loading branch information
Flurb authored Dec 8, 2021
2 parents 1c432d6 + 1f60bd2 commit 0f1bfb5
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.lfenergy.compas.scl.auto.alignment.rest.UserInfoProperties;
import org.lfenergy.compas.scl.auto.alignment.rest.v1.model.SclAutoAlignRequest;
import org.lfenergy.compas.scl.auto.alignment.rest.v1.model.SclAutoAlignResponse;
import org.lfenergy.compas.scl.auto.alignment.rest.v1.model.SclAutoAlignSVGRequest;
import org.lfenergy.compas.scl.auto.alignment.service.SclAutoAlignmentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -48,15 +49,15 @@ public SclAutoAlignResponse updateSCL(@Valid SclAutoAlignRequest request) {
LOGGER.trace("Username used for Who {}", who);

var response = new SclAutoAlignResponse();
response.setSclData(sclAutoAlignmentService.updateSCL(request.getSclData(), request.getSubstationName(), who));
response.setSclData(sclAutoAlignmentService.updateSCL(request.getSclData(), request.getSubstationNames(), who));
return response;
}

@POST
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_SVG_XML)
@Path("/svg")
public String getSVG(@Valid SclAutoAlignRequest request) {
public String getSVG(@Valid SclAutoAlignSVGRequest request) {
return sclAutoAlignmentService.getSVG(request.getSclData(), request.getSubstationName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
import org.eclipse.microprofile.openapi.annotations.media.Schema;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;

import static org.lfenergy.compas.scl.auto.alignment.SclAutoAlignmentConstants.SCL_AUTO_ALIGNMENT_SERVICE_V1_NS_URI;

Expand All @@ -19,21 +22,21 @@
@XmlAccessorType(XmlAccessType.FIELD)
public class SclAutoAlignRequest {
@Schema(description = "")
@NotBlank
@NotEmpty
@XmlElement(name = "SubstationName", namespace = SCL_AUTO_ALIGNMENT_SERVICE_V1_NS_URI)
protected String substationName;
protected List<String> substationNames = new ArrayList<>();

@Schema(description = "")
@NotBlank
@XmlElement(name = "SclData", namespace = SCL_AUTO_ALIGNMENT_SERVICE_V1_NS_URI)
protected String sclData;

public String getSubstationName() {
return substationName;
public List<String> getSubstationNames() {
return substationNames;
}

public void setSubstationName(String substationName) {
this.substationName = substationName;
public void setSubstationNames(List<String> substationNames) {
this.substationNames = substationNames;
}

public String getSclData() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2021 Alliander N.V.
//
// SPDX-License-Identifier: Apache-2.0

package org.lfenergy.compas.scl.auto.alignment.rest.v1.model;

import org.eclipse.microprofile.openapi.annotations.media.Schema;

import javax.validation.constraints.NotBlank;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import static org.lfenergy.compas.scl.auto.alignment.SclAutoAlignmentConstants.SCL_AUTO_ALIGNMENT_SERVICE_V1_NS_URI;

@Schema(description = "")
@XmlRootElement(name = "SclAutoAlignRequest", namespace = SCL_AUTO_ALIGNMENT_SERVICE_V1_NS_URI)
@XmlAccessorType(XmlAccessType.FIELD)
public class SclAutoAlignSVGRequest {
@Schema(description = "")
@NotBlank
@XmlElement(name = "SubstationName", namespace = SCL_AUTO_ALIGNMENT_SERVICE_V1_NS_URI)
protected String substationName;

@Schema(description = "")
@NotBlank
@XmlElement(name = "SclData", namespace = SCL_AUTO_ALIGNMENT_SERVICE_V1_NS_URI)
protected String sclData;

public String getSubstationName() {
return substationName;
}

public void setSubstationName(String substationName) {
this.substationName = substationName;
}

public String getSclData() {
return sclData;
}

public void setSclData(String sclData) {
this.sclData = sclData;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;
import org.lfenergy.compas.scl.auto.alignment.rest.v1.model.SclAutoAlignRequest;
import org.lfenergy.compas.scl.auto.alignment.rest.v1.model.SclAutoAlignSVGRequest;
import org.lfenergy.compas.scl.auto.alignment.service.SclAutoAlignmentService;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

import static io.restassured.RestAssured.given;
import static io.restassured.path.xml.config.XmlPathConfig.xmlPathConfig;
Expand Down Expand Up @@ -44,12 +46,13 @@ class SclAutoAlignmentResourceTest {

@Test
void updateSCL_WhenCalled_ThenExpectedResponseIsRetrieved() throws IOException {
var names = List.of(SUBSTATION_NAME);
var request = new SclAutoAlignRequest();
request.setSubstationName(SUBSTATION_NAME);
request.setSubstationNames(names);
request.setSclData(readFile());

var expectedResult = "SCL XML";
when(sclAutoAlignmentService.updateSCL(any(), eq(SUBSTATION_NAME), eq(USERNAME))).thenReturn(expectedResult);
when(sclAutoAlignmentService.updateSCL(any(), eq(names), eq(USERNAME))).thenReturn(expectedResult);

var response = given()
.contentType(ContentType.XML)
Expand All @@ -66,12 +69,12 @@ void updateSCL_WhenCalled_ThenExpectedResponseIsRetrieved() throws IOException {
var scl = xmlPath.getString("saa:SclAutoAlignmentResponse.SclData");
assertNotNull(scl);
assertEquals(expectedResult, scl);
verify(sclAutoAlignmentService, times(1)).updateSCL(any(), eq(SUBSTATION_NAME), eq(USERNAME));
verify(sclAutoAlignmentService, times(1)).updateSCL(any(), eq(names), eq(USERNAME));
}

@Test
void getSVG_WhenCalled_ThenExpectedResponseIsRetrieved() throws IOException {
var request = new SclAutoAlignRequest();
var request = new SclAutoAlignSVGRequest();
request.setSubstationName(SUBSTATION_NAME);
request.setSclData(readFile());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2021 Alliander N.V.
//
// SPDX-License-Identifier: Apache-2.0
package org.lfenergy.compas.scl.auto.alignment.rest.v1.model;

class SclAutoAlignSVGRequestTest extends AbstractPojoTester {
@Override
protected Class<?> getClassToBeTested() {
return SclAutoAlignSVGRequest.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,55 @@
// SPDX-License-Identifier: Apache-2.0
package org.lfenergy.compas.scl.auto.alignment.common;

import java.util.regex.Pattern;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;

import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.lfenergy.compas.scl.auto.alignment.SclAutoAlignmentConstants.SCLXY_NS_URI;

/**
* Some common methods used in the Auto Alignment Service.
*/
public class CommonUtil {
CommonUtil() {
throw new UnsupportedOperationException("CommonUtil class");
}

public static String cleanSXYDeclarationAndAttributes(String data) {
// Find Prefix of Namespace.
var pattern = Pattern.compile("xmlns:([A-Za-z0-9]*)=\\\"" + SCLXY_NS_URI + "\\\"");
var matcher = pattern.matcher(data);
/**
* Remove attributes from the element related to the namespace SCLXY_NS_URI
* {@link org.lfenergy.compas.scl.auto.alignment.SclAutoAlignmentConstants}.
* It will also remove the attributes from all child elements.
*
* @param element The Element to start from removing the attributes.
*/
public static void cleanSXYDeclarationAndAttributes(Element element) {
// First collect the attributes to be removed.
var attributes = element.getAttributes();
var attributesToRemove = IntStream.range(0, attributes.getLength())
.mapToObj(attributes::item)
.filter(Attr.class::isInstance)
.map(Attr.class::cast)
.filter(attr -> SCLXY_NS_URI.equals(attr.getNamespaceURI()))
.collect(Collectors.toList());
// Remove the attribute from the element.
attributesToRemove.forEach(element::removeAttributeNode);

// Check if there is a declaration that can be removed
IntStream.range(0, attributes.getLength())
.mapToObj(attributes::item)
.filter(Attr.class::isInstance)
.map(Attr.class::cast)
.filter(attr -> SCLXY_NS_URI.equals(attr.getValue()))
.forEach(element::removeAttributeNode);

if (matcher.find()) {
var prefix = matcher.group(1);
var replacementPattern = "xmlns:[A-Za-z0-9]*=\\\"" + SCLXY_NS_URI + "\\\"" + // Remove the namespace declaration.
"|" + // Combine the two regex patterns.
"[ ]?" + prefix + ":[A-Za-z]*=\\\"[A-Za-z0-9]*\\\""; // Remove the attributes using that namespace.
return data.replaceAll(replacementPattern, "");
}
return data;
// Next cleanup all the child elements in the same way.
var nodes = element.getChildNodes();
IntStream.range(0, nodes.getLength())
.mapToObj(nodes::item)
.filter(Element.class::isInstance)
.map(Element.class::cast)
.forEach(CommonUtil::cleanSXYDeclarationAndAttributes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

import java.util.concurrent.atomic.AtomicLong;

import static org.lfenergy.compas.scl.auto.alignment.common.CommonUtil.cleanSXYDeclarationAndAttributes;

public class SclAutoAlignmentEnricher {
private final GenericSCL scl;
private final String jsonGraphInfo;
Expand All @@ -25,6 +27,10 @@ public void enrich() {
var substationName = jsonSubstation.get("substationId").getAsString();
var sclSubstation = scl.getSubstation(substationName);
sclSubstation.ifPresent(substation -> {
// First we will remove all old information from this Substation.
cleanSXYDeclarationAndAttributes(substation.getElement());

// Next process the VoltageLevels.
if (jsonSubstation.has("voltageLevels")) {
jsonSubstation.getAsJsonArray("voltageLevels")
.forEach(jsonVoltageLevel -> enrichVoltageLevel(substation, jsonVoltageLevel.getAsJsonObject()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;

import static org.lfenergy.compas.scl.auto.alignment.SclAutoAlignmentConstants.SCL_ELEMENT_NAME;
import static org.lfenergy.compas.scl.auto.alignment.SclAutoAlignmentConstants.SCL_NS_URI;
import static org.lfenergy.compas.scl.auto.alignment.common.CommonUtil.cleanSXYDeclarationAndAttributes;
import static org.lfenergy.compas.scl.auto.alignment.exception.SclAutoAlignmentErrorCode.NO_SCL_ELEMENT_FOUND_ERROR_CODE;
import static org.lfenergy.compas.scl.auto.alignment.exception.SclAutoAlignmentErrorCode.SUBSTATION_NOT_FOUND_ERROR_CODE;

Expand All @@ -39,15 +39,18 @@ public SclAutoAlignmentService(ElementConverter converter) {
this.converter = converter;
}

public String updateSCL(String sclData, String substationName, String who) {
public String updateSCL(String sclData, List<String> substationNames, String who) {
var scl = readSCL(sclData);
var substationBuilder = createSubstationBuilder(scl.getSubstation(substationName), substationName);

// Create the JSON With all X/Y Coordinate information.
var jsonGraphInfo = createJson(substationBuilder);
// Use that JSON to enrich the passed SCL XML with X/Y Coordinates.
var enricher = new SclAutoAlignmentEnricher(scl, jsonGraphInfo);
enricher.enrich();
substationNames.forEach(substationName -> {
var substationBuilder = createSubstationBuilder(scl.getSubstation(substationName), substationName);

// Create the JSON With all X/Y Coordinate information.
var jsonGraphInfo = createJson(substationBuilder);
// Use that JSON to enrich the passed SCL XML with X/Y Coordinates.
var enricher = new SclAutoAlignmentEnricher(scl, jsonGraphInfo);
enricher.enrich();
});

// Add an extra History Element to show there was a change.
scl.getOrCreateHeader().addHistoryItem(who, "Add or replaced the X/Y Coordinates in the SCL File.");
Expand All @@ -63,12 +66,9 @@ public String getSVG(String sclData, String substationName) {
}

GenericSCL readSCL(String sclData) {
// First we will cleanup existing X/Y Coordinates from the SCL XML.
var cleanSclData = cleanSXYDeclarationAndAttributes(sclData);

// Next convert the String to W3C Document/Element
var sclElement = converter.convertToElement(new BufferedInputStream(
new ByteArrayInputStream(cleanSclData.getBytes(StandardCharsets.UTF_8))), SCL_ELEMENT_NAME, SCL_NS_URI);
new ByteArrayInputStream(sclData.getBytes(StandardCharsets.UTF_8))), SCL_ELEMENT_NAME, SCL_NS_URI);
if (sclElement == null) {
throw new SclAutoAlignmentException(NO_SCL_ELEMENT_FOUND_ERROR_CODE, "No valid SCL found in the passed SCL Data.");
}
Expand Down
Loading

0 comments on commit 0f1bfb5

Please sign in to comment.