Skip to content

Commit

Permalink
Merge pull request #78 from useocl/77-usemodelapi-lacks-of-self-for-a…
Browse files Browse the repository at this point in the history
…ttributes-in-ocl-expression

fix!: UseModelApi: Lacks of self for attributes in OCL Expression
  • Loading branch information
h-man2 authored Jun 6, 2024
2 parents 01330ff + 0757ef0 commit 2cfebf1
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 30 deletions.
81 changes: 51 additions & 30 deletions use-core/src/main/java/org/tzi/use/api/UseModelApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
*/
package org.tzi.use.api;

import org.tzi.use.parser.Context;
import org.tzi.use.parser.SemanticException;
import org.tzi.use.parser.SrcPos;
import org.tzi.use.parser.Symtable;
import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.tzi.use.parser.*;
import org.tzi.use.parser.generator.GeneratorLexer;
import org.tzi.use.parser.generator.GeneratorParser;
import org.tzi.use.parser.ocl.ASTExpression;
import org.tzi.use.parser.ocl.OCLCompiler;
import org.tzi.use.parser.soil.SoilCompiler;
import org.tzi.use.parser.soil.ast.ASTStatement;
Expand All @@ -41,10 +44,7 @@
import org.tzi.use.util.StringUtil;
import org.tzi.use.util.soil.exceptions.CompilationFailedException;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -498,15 +498,15 @@ public MOperation createImperativeOperation(String ownerName, String operationNa
* @param condition The OCL-Expression of the condition.
* @param isPre Switch whether the condition is a precondition or not.
* @return The created {@link MPrePostCondition}.
* @throws UseApiException
* @throws UseApiException If the condition could not be created.
*/
public MPrePostCondition createPrePostCondition(String ownerName,
String operationName, String name, String condition, boolean isPre)
throws UseApiException {
MClass cls = getClassSafe(ownerName);
MOperation op = cls.operation(operationName, false);

if(op == null){
if(op == null) {
throw new UseApiException("Unknown operation "
+ StringUtil.inQuotes(ownerName + "::" + operationName)
+ ".");
Expand All @@ -515,30 +515,51 @@ public MPrePostCondition createPrePostCondition(String ownerName,
StringWriter errBuffer = new StringWriter();
PrintWriter errorPrinter = new PrintWriter(errBuffer, true);

Symtable symTable = new Symtable();
ParseErrorHandler errHandler = new ParseErrorHandler("UseModelApi", errorPrinter);
InputStream inStream = new ByteArrayInputStream(condition.getBytes());

Expression exp = null;

try {
symTable.add("self", cls, null);
for(VarDecl var : op.paramList()){
symTable.add(var.name(), var.type(), null);
}
if(op.hasResultType()){
symTable.add("result", op.resultType(), null);
}
}
catch(SemanticException ex){
throw new UseApiException("Could not create pre-/postcondition.", ex);
}
ANTLRInputStream aInput = new ANTLRInputStream(inStream);
aInput.name = "UseModelApi";

Expression conditionExp = OCLCompiler.compileExpression(mModel,
condition, "condition", errorPrinter, symTable);
GeneratorLexer lexer = new GeneratorLexer(aInput);
CommonTokenStream tStream = new CommonTokenStream(lexer);
GeneratorParser parser = new GeneratorParser(tStream);

if (conditionExp == null) {
throw new UseApiException(
"Compilation of condition expression failed:\n"
+ errBuffer.toString());
}
lexer.init(errHandler);
parser.init(errHandler);

// Parse the specification
ASTExpression astCondition = parser.expressionOnly();

if (errHandler.errorCount() == 0 ) {
ModelFactory modelFactory = new ModelFactory();
Context ctx = new Context("UseModelApi",
errorPrinter,
null,
modelFactory);
ctx.setModel(this.getModel());

Symtable vars = ctx.varTable();

// create pseudo-variable "self"
vars.add("self", cls, null);
ctx.exprContext().push("self", cls);
// add special variable `result' in postconditions with result value
if (! isPre && op.hasResultType() )
vars.add("result", op.resultType(), null);

ctx.setInsidePostCondition(! isPre);

exp = astCondition.gen(ctx);
}
} catch (RecognitionException | SemanticException | IOException e) {
throw new UseApiException("Error adding condition!", e);
}

return createPrePostConditionEx(name, op, isPre, conditionExp);
return this.createPrePostConditionEx(name, op,isPre, exp);
}

/**
Expand Down
35 changes: 35 additions & 0 deletions use-core/src/test/java/org/tzi/use/uml/mm/ModelAPITest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.tzi.use.uml.mm;

import org.junit.jupiter.api.Test;
import org.tzi.use.api.UseApiException;
import org.tzi.use.api.UseModelApi;

import static org.junit.jupiter.api.Assertions.*;

public class ModelAPITest {
@Test
public void testConstraintCreation() throws UseApiException {
UseModelApi api = new UseModelApi("UnitTest");
MPrePostCondition ppc;

api.createClass("A", false);
api.createAttribute("A", "foo", "String");
api.createOperation("A", "bar", new String[0][0], "String");
ppc = api.createPrePostCondition("A", "bar", "self.foo is defined", "self.foo.isDefined()", true);
assertEquals("self.foo is defined", ppc.name());

ppc = api.createPrePostCondition("A", "bar", "foo without self is defined", "foo.isDefined()", true);
assertEquals("foo without self is defined", ppc.name());

assertEquals(2, api.getClass("A").operation("bar", true).preConditions().size());

api.createPrePostCondition("A", "bar", "result can be checked", "result.isDefined()", false);

assertEquals(1, api.getClass("A").operation("bar", true).postConditions().size());

Exception ex = assertThrows(UseApiException.class, ()
-> api.createPrePostCondition("B", "", "", "", true));

assertTrue(ex.getMessage().contains("Unknown"));
}
}

0 comments on commit 2cfebf1

Please sign in to comment.