Skip to content

Commit e8e12af

Browse files
author
Julia Pham
committed
fix: merge main into issue-22
2 parents cc5a21f + 4f24870 commit e8e12af

10 files changed

Lines changed: 291 additions & 72 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
<dependency>
8080
<groupId>io.github.universal-variability-language</groupId>
8181
<artifactId>uvl-parser</artifactId>
82-
<version>0.4.1</version>
82+
<version>0.5.0</version>
8383
</dependency>
8484
<dependency>
8585
<groupId>org.junit.jupiter</groupId>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package de.vill.main;
2+
3+
import java.util.ArrayDeque;
4+
import java.util.Deque;
5+
6+
import org.antlr.v4.runtime.tree.ErrorNode;
7+
import org.antlr.v4.runtime.tree.ParseTree;
8+
import org.antlr.v4.runtime.tree.ParseTreeListener;
9+
import org.antlr.v4.runtime.tree.ParseTreeWalker;
10+
import org.antlr.v4.runtime.tree.RuleNode;
11+
import org.antlr.v4.runtime.tree.TerminalNode;
12+
13+
/**
14+
* Iterative variant of ANTLR's ParseTreeWalker.
15+
*
16+
* ANTLR's default ParseTreeWalker recursively walks the parse tree.
17+
* Deeply nested constraints such as F0 | F1 | ... | F6999 can therefore
18+
* overflow the Java call stack before the UVL model is constructed.
19+
*/
20+
final class IterativeParseTreeWalker extends ParseTreeWalker {
21+
22+
@Override
23+
public void walk(ParseTreeListener listener, ParseTree tree) {
24+
final Deque<Frame> stack = new ArrayDeque<>();
25+
stack.push(new Frame(tree));
26+
27+
while (!stack.isEmpty()) {
28+
final Frame frame = stack.peek();
29+
final ParseTree current = frame.tree;
30+
31+
if (current instanceof ErrorNode) {
32+
listener.visitErrorNode((ErrorNode) current);
33+
stack.pop();
34+
continue;
35+
}
36+
37+
if (current instanceof TerminalNode) {
38+
listener.visitTerminal((TerminalNode) current);
39+
stack.pop();
40+
continue;
41+
}
42+
43+
final RuleNode ruleNode = (RuleNode) current;
44+
45+
if (!frame.entered) {
46+
enterRule(listener, ruleNode);
47+
frame.entered = true;
48+
}
49+
50+
if (frame.nextChildIndex < current.getChildCount()) {
51+
stack.push(new Frame(current.getChild(frame.nextChildIndex)));
52+
frame.nextChildIndex++;
53+
} else {
54+
exitRule(listener, ruleNode);
55+
stack.pop();
56+
}
57+
}
58+
}
59+
60+
private static final class Frame {
61+
private final ParseTree tree;
62+
private boolean entered;
63+
private int nextChildIndex;
64+
65+
private Frame(ParseTree tree) {
66+
this.tree = tree;
67+
}
68+
}
69+
}

src/main/java/de/vill/main/UVLListener.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,15 @@
5353
import org.antlr.v4.runtime.Token;
5454

5555
public class UVLListener extends UVLJavaParserBaseListener {
56-
public FeatureModelBuilder fmBuilder = new FeatureModelBuilder();
56+
public FeatureModelBuilder fmBuilder;
57+
58+
public UVLListener() {
59+
this(new FeatureModelBuilder());
60+
}
61+
62+
public UVLListener(FeatureModelBuilder fmBuilder) {
63+
this.fmBuilder = fmBuilder;
64+
}
5765
private Set<LanguageLevel> importedLanguageLevels = new HashSet<>(Arrays.asList(LanguageLevel.BOOLEAN_LEVEL));
5866
private Stack<Feature> featureStack = new Stack<>();
5967
private Stack<Group> groupStack = new Stack<>();
@@ -407,7 +415,13 @@ public void exitNotConstraint(UVLJavaParser.NotConstraintContext ctx) {
407415
public void exitAndConstraint(UVLJavaParser.AndConstraintContext ctx) {
408416
Constraint rightConstraint = constraintStack.pop();
409417
Constraint leftConstraint = constraintStack.pop();
410-
Constraint constraint = new AndConstraint(leftConstraint, rightConstraint);
418+
Constraint constraint;
419+
if (leftConstraint instanceof AndConstraint) {
420+
((AndConstraint) leftConstraint).addChild(rightConstraint);
421+
constraint = leftConstraint;
422+
} else {
423+
constraint = new AndConstraint(leftConstraint, rightConstraint);
424+
}
411425
constraintStack.push(constraint);
412426
Token t = ctx.getStart();
413427
int line = t.getLine();
@@ -418,7 +432,13 @@ public void exitAndConstraint(UVLJavaParser.AndConstraintContext ctx) {
418432
public void exitOrConstraint(UVLJavaParser.OrConstraintContext ctx) {
419433
Constraint rightConstraint = constraintStack.pop();
420434
Constraint leftConstraint = constraintStack.pop();
421-
Constraint constraint = new OrConstraint(leftConstraint, rightConstraint);
435+
Constraint constraint;
436+
if (leftConstraint instanceof OrConstraint) {
437+
((OrConstraint) leftConstraint).addChild(rightConstraint);
438+
constraint = leftConstraint;
439+
} else {
440+
constraint = new OrConstraint(leftConstraint, rightConstraint);
441+
}
422442
constraintStack.push(constraint);
423443
Token t = ctx.getStart();
424444
int line = t.getLine();

src/main/java/de/vill/main/UVLModelFactory.java

Lines changed: 89 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package de.vill.main;
22

33
import de.vill.model.*;
4+
import de.vill.model.building.AbstractUVLElementFactory;
5+
import de.vill.model.building.DefaultUVLElementFactory;
6+
import de.vill.model.building.FeatureModelBuilder;
47
import de.vill.model.building.VariableReference;
58
import de.vill.model.constraint.*;
69
import de.vill.model.expression.*;
@@ -34,7 +37,6 @@
3437
import org.antlr.v4.runtime.ConsoleErrorListener;
3538
import org.antlr.v4.runtime.RecognitionException;
3639
import org.antlr.v4.runtime.Recognizer;
37-
import org.antlr.v4.runtime.tree.ParseTreeWalker;
3840

3941
import java.io.IOException;
4042
import java.nio.file.FileSystems;
@@ -47,10 +49,16 @@ public class UVLModelFactory {
4749

4850
private final Map<LanguageLevel, Class<? extends IConversionStrategy>> conversionStrategiesDrop;
4951
private final Map<LanguageLevel, Class<? extends IConversionStrategy>> conversionStrategiesConvert;
52+
private final AbstractUVLElementFactory elementFactory;
5053

5154
private final List<ParseError> errorList = new LinkedList<>();
5255

5356
public UVLModelFactory() {
57+
this(new DefaultUVLElementFactory());
58+
}
59+
60+
public UVLModelFactory(AbstractUVLElementFactory elementFactory) {
61+
this.elementFactory = elementFactory;
5462
this.conversionStrategiesDrop = new HashMap<>();
5563
this.conversionStrategiesDrop.put(LanguageLevel.GROUP_CARDINALITY, DropGroupCardinality.class);
5664
this.conversionStrategiesDrop.put(LanguageLevel.FEATURE_CARDINALITY, DropFeatureCardinality.class);
@@ -119,8 +127,9 @@ public Constraint parseConstraint(String constraintString) throws ParseError {
119127
UVLJavaLexer.addErrorListener(listener);
120128
UVLJavaParser.addErrorListener(listener);
121129

122-
UVLListener uvlListener = new UVLListener();
123-
ParseTreeWalker walker = new ParseTreeWalker();
130+
UVLListener uvlListener = createUVLListener();
131+
IterativeParseTreeWalker walker = new IterativeParseTreeWalker();
132+
124133
walker.walk(uvlListener, UVLJavaParser.constraintLine());
125134

126135
return uvlListener.getConstraint();
@@ -267,6 +276,10 @@ private List<LanguageLevel> getActualLanguageLevelsToRemoveInOrder(FeatureModel
267276
return completeOrderedLevelsToRemove;
268277
}
269278

279+
private UVLListener createUVLListener() {
280+
return new UVLListener(new FeatureModelBuilder(elementFactory));
281+
}
282+
270283
private String getPath(String rootPath, Import referencedImport) {
271284
return rootPath + FileSystems.getDefault().getSeparator() + referencedImport.getNamespace().replace(".", FileSystems.getDefault().getSeparator()) + ".uvl";
272285
}
@@ -285,8 +298,10 @@ private FeatureModel parseFeatureModelWithImports(String text, String rootPath,
285298
UVLJavaParser.addErrorListener(listener);
286299

287300

288-
UVLListener uvlListener = new UVLListener();
289-
ParseTreeWalker walker = new ParseTreeWalker();
301+
302+
UVLListener uvlListener = createUVLListener();
303+
IterativeParseTreeWalker walker = new IterativeParseTreeWalker();
304+
290305
walker.walk(uvlListener, UVLJavaParser.featureModel());
291306
FeatureModel featureModel = null;
292307

@@ -378,42 +393,55 @@ private FeatureModel parseFeatureModelWithImports(String text, String rootPath,
378393
}
379394

380395
private void resolveImportPlaceholders(Constraint constraint, FeatureModel featureModel) {
381-
if (constraint instanceof AndConstraint || constraint instanceof OrConstraint || constraint instanceof NotConstraint || constraint instanceof ImplicationConstraint || constraint instanceof ParenthesisConstraint || constraint instanceof EquivalenceConstraint) {
382-
for (Constraint subPart : constraint.getConstraintSubParts()) {
383-
resolveImportPlaceholders(subPart, featureModel);
396+
final Deque<Constraint> stack = new ArrayDeque<>();
397+
stack.push(constraint);
398+
399+
while (!stack.isEmpty()) {
400+
final Constraint current = stack.pop();
401+
402+
if (current instanceof ExpressionConstraint) {
403+
ExpressionConstraint expressionConstraint = (ExpressionConstraint) current;
404+
resolveImportPlaceholders(expressionConstraint.getLeft(), featureModel);
405+
resolveImportPlaceholders(expressionConstraint.getRight(), featureModel);
406+
} else if (current instanceof LiteralConstraint) {
407+
LiteralConstraint literalConstraint = (LiteralConstraint) current;
408+
if (literalConstraint.getReference() instanceof ImportedVariablePlaceholder) {
409+
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) literalConstraint.getReference();
410+
literalConstraint.setReference(resolvePlaceholder(placeholder, featureModel));
411+
}
384412
}
385-
} else if (constraint instanceof ExpressionConstraint) {
386-
ExpressionConstraint expressionConstraint = (ExpressionConstraint) constraint;
387-
resolveImportPlaceholders(expressionConstraint.getLeft(), featureModel);
388-
resolveImportPlaceholders(expressionConstraint.getRight(), featureModel);
389-
} else if (constraint instanceof LiteralConstraint) {
390-
LiteralConstraint literalConstraint = (LiteralConstraint) constraint;
391-
if (literalConstraint.getReference() instanceof ImportedVariablePlaceholder) {
392-
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) literalConstraint.getReference();
393-
literalConstraint.setReference(resolvePlaceholder(placeholder, featureModel));
413+
414+
final List<Constraint> subConstraints = current.getConstraintSubParts();
415+
for (int i = subConstraints.size() - 1; i >= 0; i--) {
416+
stack.push(subConstraints.get(i));
394417
}
395418
}
396419
}
397420

398421
private void resolveImportPlaceholders(Expression expression, FeatureModel featureModel) {
399-
if (expression instanceof BinaryExpression) {
400-
BinaryExpression binaryExpression = (BinaryExpression) expression;
401-
resolveImportPlaceholders(binaryExpression.getLeft(), featureModel);
402-
resolveImportPlaceholders(binaryExpression.getRight(), featureModel);
403-
} else if (expression instanceof ParenthesisExpression) {
404-
ParenthesisExpression parenthesisExpression = (ParenthesisExpression) expression;
405-
resolveImportPlaceholders(parenthesisExpression.getContent(), featureModel);
406-
} else if (expression instanceof LengthAggregateFunctionExpression) {
407-
LengthAggregateFunctionExpression lengthAggregateFunctionExpression = (LengthAggregateFunctionExpression) expression;
408-
if (lengthAggregateFunctionExpression.getReference() instanceof ImportedVariablePlaceholder) {
409-
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) lengthAggregateFunctionExpression.getReference();
410-
lengthAggregateFunctionExpression.setReference(resolvePlaceholder(placeholder, featureModel));
422+
final Deque<Expression> stack = new ArrayDeque<>();
423+
stack.push(expression);
424+
425+
while (!stack.isEmpty()) {
426+
final Expression current = stack.pop();
427+
428+
if (current instanceof LengthAggregateFunctionExpression) {
429+
LengthAggregateFunctionExpression lengthAggregateFunctionExpression = (LengthAggregateFunctionExpression) current;
430+
if (lengthAggregateFunctionExpression.getReference() instanceof ImportedVariablePlaceholder) {
431+
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) lengthAggregateFunctionExpression.getReference();
432+
lengthAggregateFunctionExpression.setReference(resolvePlaceholder(placeholder, featureModel));
433+
}
434+
} else if (current instanceof LiteralExpression) {
435+
LiteralExpression literalExpression = (LiteralExpression) current;
436+
if (literalExpression.getContent() instanceof ImportedVariablePlaceholder) {
437+
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) literalExpression.getContent();
438+
literalExpression.setContent(resolvePlaceholder(placeholder, featureModel));
439+
}
411440
}
412-
} else if (expression instanceof LiteralExpression) {
413-
LiteralExpression literalExpression = (LiteralExpression) expression;
414-
if (literalExpression.getContent() instanceof ImportedVariablePlaceholder) {
415-
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) literalExpression.getContent();
416-
literalExpression.setContent(resolvePlaceholder(placeholder, featureModel));
441+
442+
final List<Expression> subExpressions = current.getExpressionSubParts();
443+
for (int i = subExpressions.size() - 1; i >= 0; i--) {
444+
stack.push(subExpressions.get(i));
417445
}
418446
}
419447
}
@@ -537,28 +565,38 @@ private void validateTypeLevelConstraints(final FeatureModel featureModel) {
537565
}
538566

539567
private boolean validateTypeLevelConstraint(final Constraint constraint) {
540-
boolean result = true;
541-
if (constraint instanceof ExpressionConstraint) {
542-
String leftReturnType = ((ExpressionConstraint) constraint).getLeft().getReturnType();
543-
String rightReturnType = ((ExpressionConstraint) constraint).getRight().getReturnType();
568+
final Deque<Constraint> stack = new ArrayDeque<>();
569+
stack.push(constraint);
544570

545-
if (!(leftReturnType.equalsIgnoreCase(Constants.TRUE) || rightReturnType.equalsIgnoreCase(Constants.TRUE))) {
546-
// if not attribute constraint
547-
result = result && ((ExpressionConstraint) constraint).getLeft().getReturnType().equalsIgnoreCase(((ExpressionConstraint) constraint).getRight().getReturnType());
548-
}
549-
if (!result) {
550-
return false;
551-
}
552-
for (final Expression expr: ((ExpressionConstraint) constraint).getExpressionSubParts()) {
553-
result = result && validateTypeLevelExpression(expr);
571+
while (!stack.isEmpty()) {
572+
final Constraint current = stack.pop();
573+
574+
if (current instanceof ExpressionConstraint) {
575+
final ExpressionConstraint expressionConstraint = (ExpressionConstraint) current;
576+
577+
final String leftReturnType = expressionConstraint.getLeft().getReturnType();
578+
final String rightReturnType = expressionConstraint.getRight().getReturnType();
579+
580+
if (!(leftReturnType.equalsIgnoreCase(Constants.TRUE) || rightReturnType.equalsIgnoreCase(Constants.TRUE))) {
581+
if (!leftReturnType.equalsIgnoreCase(rightReturnType)) {
582+
return false;
583+
}
584+
}
585+
586+
for (final Expression expr : expressionConstraint.getExpressionSubParts()) {
587+
if (!validateTypeLevelExpression(expr)) {
588+
return false;
589+
}
590+
}
554591
}
555-
}
556592

557-
for (final Constraint subCons: constraint.getConstraintSubParts()) {
558-
result = result && validateTypeLevelConstraint(subCons);
593+
final List<Constraint> subConstraints = current.getConstraintSubParts();
594+
for (int i = subConstraints.size() - 1; i >= 0; i--) {
595+
stack.push(subConstraints.get(i));
596+
}
559597
}
560598

561-
return result;
599+
return true;
562600
}
563601

564602
private boolean validateTypeLevelExpression(final Expression expression) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package de.vill.model.building;
2+
3+
import de.vill.exception.ParseError;
4+
import de.vill.model.*;
5+
import de.vill.model.constraint.LiteralConstraint;
6+
7+
public abstract class AbstractUVLElementFactory {
8+
9+
public abstract Feature createFeature(String name);
10+
11+
public abstract <T> Attribute<T> createAttribute(String name, T value, Feature correspondingFeature);
12+
13+
public abstract GlobalAttribute createGlobalAttribute(String identifier, FeatureModel featureModel);
14+
15+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package de.vill.model.building;
2+
3+
import de.vill.exception.ParseError;
4+
import de.vill.model.*;
5+
import de.vill.model.constraint.LiteralConstraint;
6+
7+
public class DefaultUVLElementFactory extends AbstractUVLElementFactory {
8+
9+
@Override
10+
public Feature createFeature(String name) {
11+
return new Feature(name);
12+
}
13+
14+
@Override
15+
public <T> Attribute<T> createAttribute(String name, T value, Feature correspondingFeature) {
16+
return new Attribute<>(name, value, correspondingFeature);
17+
}
18+
19+
@Override
20+
public GlobalAttribute createGlobalAttribute(String identifier, FeatureModel featureModel) {
21+
return new GlobalAttribute(identifier, featureModel);
22+
}
23+
}

0 commit comments

Comments
 (0)