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,72 @@
/* (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.symboltable.modifiers.AccessModifier;
import de.se_rwth.commons.logging.Log;

/**
* CoCo3: Jeder in "connect a.b to c.d" verwendete Name von Subkomponenten (a und c)
* muss eindeutig im Modell vorhanden sein.
*/
public class MKPX_CoCo3 implements SysMLPartsASTConnectionUsageCoCo {

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

var scope = node.getEnclosingScope();

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

protected void checkSubcomponentQualifier(ISysMLPartsScope scope,
ASTEndpoint endpoint,
ASTConnectionUsage node) {
String qname = endpointQName(endpoint);
String subName = extractFirstSegment(qname);

if (subName == null) {
return;
}

int matches = scope
.resolvePartUsageLocallyMany(false, subName,
AccessModifier.ALL_INCLUSION, p -> true)
.size();

if (matches > 1) {
Log.error(
"0xMKPX03 The subcomponent name used in 'connect' \"" + subName
+ "\" is not unique in the model.",
node.get_SourcePositionStart(),
node.get_SourcePositionEnd()
);
} else if (matches != 1) { /** matches = 0 */
Log.error(
"0xMKPX03 The subcomponent name used in 'connect' \"" + subName
+ "\" does not exist in the model.",
node.get_SourcePositionStart(),
node.get_SourcePositionEnd()
);
}
}

protected String endpointQName(ASTEndpoint ep) {
return ep.getMCQualifiedName() != null ? ep.getMCQualifiedName().toString() : "";
}

protected String extractFirstSegment(String qname) {
int dot = qname.indexOf('.');
if (dot > 0) {
return qname.substring(0, dot);
}
return null;
}
}
102 changes: 102 additions & 0 deletions language/src/test/java/cocos/MKPXCoCo3Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* (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.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_CoCo3;
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 MKPXCoCo3Test {

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 MKPXCoCo3Tests {
@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((SysMLPartsASTConnectionUsageCoCo) new MKPX_CoCo3());
checker.checkAll(ast);
assertTrue(Log.getFindings().isEmpty());
}

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

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

@Test
public void testInvalidDuplicateName() throws IOException {
String invalidModel =
"part def A { port p; }"
+ "part def System {"
+ "part a: A;"
+ "part a: A;"
+ "connect a.p to a.p;"
+ "}";

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