Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.sysmlv2.cocos;

import de.monticore.lang.sysmlbasis._ast.ASTEndpoint;
import de.monticore.lang.sysmlparts._ast.ASTConnectionUsage;
import de.monticore.lang.sysmlparts._cocos.SysMLPartsASTConnectionUsageCoCo;
import de.monticore.lang.sysmlparts._symboltable.ISysMLPartsScope;
import de.monticore.lang.sysmlparts._symboltable.PartDefSymbol;
import de.monticore.lang.sysmlparts._symboltable.PartUsageSymbol;
import de.monticore.symboltable.modifiers.AccessModifier;
import de.se_rwth.commons.logging.Log;

import java.util.List;
import java.util.Optional;

/**
* MKPX_CoCo4:
* In einer Verbindung "connect a.b to c.d" muss jeder verwendete (qualifizierte) Portname existieren.
*/
public class MKPX_CoCo4 implements SysMLPartsASTConnectionUsageCoCo {

@Override
public void check(ASTConnectionUsage node) {
if (!node.isPresentSrc() || !node.isPresentTgt()) {
return; // keine Verbindung
}

ISysMLPartsScope scope = node.getEnclosingScope();

checkEndpoint(scope, node.getSrc(), node);
checkEndpoint(scope, node.getTgt(), node);
}

protected void checkEndpoint(ISysMLPartsScope scope,
ASTEndpoint endpoint,
ASTConnectionUsage conn) {
String qname = endpointQName(endpoint);
if (qname.isEmpty()) {
return;
}

String[] parts = qname.split("\\.");

// Qualifizierter Name: a.b -> resolve a (PartUsage), dann b in PartDef
if (parts.length >= 2) {
String partName = parts[0];
String portName = parts[parts.length - 1];

Optional<PartUsageSymbol> partOpt = scope.resolvePartUsageLocally(partName);

if (partOpt.isEmpty()) {
// Existenz der Subkomponente wird bereits in MKPX_CoCo3 geprüft.
return;
}

var partDefOpt = partOpt.get().getPartDef();
if (partDefOpt.isEmpty()) {
// CoCo3
return;
}

PartDefSymbol partDef = partDefOpt.get();
boolean portExistsInDef = partDef.getSpannedScope()
.resolvePortUsageLocallyMany(false, portName, AccessModifier.ALL_INCLUSION, p -> true)
.size() == 1;

if (!portExistsInDef) {
Log.error(
"0xMKPX04 The port '" + portName + "' does not exist in the definition of subcomponent '"
+ partName + "'.",
conn.get_SourcePositionStart(),
conn.get_SourcePositionEnd()
);
}
}
else {
// Unqualifiziert: Port der Oberkomponente muss lokal existieren
String portName = parts[0];
List<?> localPorts = scope.resolvePortUsageLocallyMany(
false, portName, AccessModifier.ALL_INCLUSION, p -> true);
if (localPorts.isEmpty()) {
Log.error(
"0xMKPX04 The port used in 'connect' '" + portName
+ "' does not exist in the parent component.",
conn.get_SourcePositionStart(),
conn.get_SourcePositionEnd()
);
}
}
}

/** Liefert den qualifizierten Namen des Endpunkts als String. */
protected String endpointQName(ASTEndpoint ep) {
return ep.getMCQualifiedName() != null ? ep.getMCQualifiedName().toString() : "";
}
}
84 changes: 84 additions & 0 deletions language/src/test/java/cocos/MKPXCoCo4Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* (c) https://github.com/MontiCore/monticore */
package cocos;

import de.monticore.lang.sysmlparts._cocos.SysMLPartsASTConnectionUsageCoCo;
import de.monticore.lang.sysmlparts._cocos.SysMLPartsASTPartDefCoCo;
import de.monticore.lang.sysmlparts._cocos.SysMLPartsASTPartUsageCoCo;
import de.monticore.lang.sysmlv2.SysMLv2Mill;
import de.monticore.lang.sysmlv2._ast.ASTSysMLModel;
import de.monticore.lang.sysmlv2._cocos.SysMLv2CoCoChecker;
import de.monticore.lang.sysmlv2._parser.SysMLv2Parser;
import de.monticore.lang.sysmlv2.cocos.MKPX_CoCo4;
import de.se_rwth.commons.logging.Log;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class MKPXCoCo4Test {

private static final String MODEL_PATH = "src/test/resources/parser";

private SysMLv2Parser parser = SysMLv2Mill.parser();

@BeforeAll
public static void init() {
Log.init();
SysMLv2Mill.init();
}

@BeforeEach
public void reset() {
SysMLv2Mill.globalScope().clear();
Log.getFindings().clear();
Log.enableFailQuick(true);
}

@Nested
public class MKPXCoCo4Tests {
@Test
public void testValid() throws IOException {
String validModel =
"part def A { port p: int; }"
+ "part def B { port q: ~int; }"
+ "part def System {"
+ "part a: A;"
+ "part b: B;"
+ "connect a.p to b.q;"
+ "}";

ASTSysMLModel ast = SysMLv2Mill.parser().parse_String(validModel).get();
SysMLv2Mill.scopesGenitorDelegator().createFromAST(ast);
var checker = new SysMLv2CoCoChecker();
checker.addCoCo(new MKPX_CoCo4());
checker.checkAll(ast);
assertTrue(Log.getFindings().isEmpty());
}

@Test
public void testInvalid() throws IOException {
String invalidModel =
"part def A { port p: int; }"
+ "part def B { port q: ~int; }"
+ "part def System {"
+ "part a: A;"
+ "part b: B;"
+ "connect a.wrongName to b.q;"
+ "}";

ASTSysMLModel ast = SysMLv2Mill.parser().parse_String(invalidModel).get();
SysMLv2Mill.scopesGenitorDelegator().createFromAST(ast);
var checker = new SysMLv2CoCoChecker();
checker.addCoCo(new MKPX_CoCo4());
Log.enableFailQuick(false);
checker.checkAll(ast);
assertTrue(Log.getFindings().stream()
.anyMatch(f -> f.getMsg().contains("0xMKPX04")));
}
}
}
Loading