Permalink
Browse files

Merge pull request #1436 from umple/featureModelAnalysis_bracket

Feature model analysis (Bracket)
  • Loading branch information...
TimLethbridge committed Feb 1, 2019
2 parents 5bb4295 + a763062 commit bc0cac3a44f787cd495e337bc67510833604d13d
@@ -43,7 +43,7 @@ class UmpleInternalParser
boolean isSubFeature = t.getSubToken("subfeature") != null;

//tokens needed for parsing require-statement
List<String> acceptedTokensList = Arrays.asList("requireTerminal","and","not","xor","or","opt");
List<String> acceptedTokensList = Arrays.asList("requireTerminal","and","not","xor","or","opt","(",")");
ArrayList<Token> requireTokenList = getRequireStatementTokensAsList(t, acceptedTokensList);
Token firstTokenOfRequireTokenList;

@@ -210,18 +210,51 @@ class UmpleInternalParser
This method parses req-statement argument & generates a binary tree representation form the req-statment argument.
//It returns one node (root node) if there is no argument to parse.
*/
private ArrayList<TokenTree> generateFeatureTreeTokenFromRequireStList(ArrayList<Token> tokenList)
private ArrayList<TokenTree> generateFeatureTreeTokenFromRequireStList(List<Token> tokenList)
{
TokenTree rootTokenTree = new TokenTree(new Token("ROOT",""));
TokenTree currentTree = rootTokenTree;
List<String> linkingOpList = Arrays.asList("and","or","xor");
ArrayList<TokenTree> tokenTreeList = new ArrayList<TokenTree>();

int openRoundBracketCount=0;
int openBracketIndex = 0;
ArrayList<TokenTree> tempTokenTreeList = new ArrayList<TokenTree>();

for (int i=0;i<tokenList.size();i++)
{
Token token = tokenList.get(i);
TokenTree rightTokenTree = new TokenTree(token);
//Start(1): put each [opt/not][terminal] in a separate TokenTree if it was preceded with [terminal]
if(linkingOpList.contains(token.getName()))
rightTokenTree.setIsLinkingOperator(true);
//Start(1): handel open_round_bracket by assigning a temp variable to avoid processing it when building the binary tree.
//each temp variable will be replaced before method return.
if(token.getName().equals("("))
{
openRoundBracketCount++;
if(openRoundBracketCount == 1)
openBracketIndex = i;
continue;
}
else if (token.getName().equals(")"))
{
openRoundBracketCount--;
if(openRoundBracketCount == 0) //last close
{
ArrayList<TokenTree> temp = generateFeatureTreeTokenFromRequireStList(tokenList.subList(openBracketIndex+1,i));
if(temp.size() == 1)
{
Token tempToken = new Token("requireTerminal","temp_"+tempTokenTreeList.size());
tempTokenTreeList.add(temp.get(0));
rightTokenTree.setNodeToken(tempToken);
}
}
else
continue;
}
else if(openRoundBracketCount >0) //there was a bracket
continue;
//End(1)
//Start(2): put each [opt/not][terminal] in a separate TokenTree if it was preceded with [terminal]
if(token.is("opt") || token.is("not"))
{
if(currentTree.getNodeToken().is("requireTerminal") && tokenList.get(i+1).is("requireTerminal")) // A opt B
@@ -233,17 +266,15 @@ private ArrayList<TokenTree> generateFeatureTreeTokenFromRequireStList(ArrayList
continue;
}
}
//End(1)
if(linkingOpList.contains(token.getName()))
rightTokenTree.setIsLinkingOperator(true);
//End(2)
currentTree.setRightTokenTree(rightTokenTree);
rightTokenTree.setParentTokenTree(currentTree);
currentTree = rightTokenTree;
}
currentTree = rootTokenTree.getRightTokenTree(); //currentTree points to the first node of the tree

TokenTree previousLinkingSubTokenTree = null;
//Start(3): building the binary tree of re-st argument
while(currentTree.getRightTokenTree() != null)
{
TokenTree rightLinkingTokenTree = currentTree.getRightTokenTree(); //linking operator on the right of current node
@@ -301,9 +332,46 @@ private ArrayList<TokenTree> generateFeatureTreeTokenFromRequireStList(ArrayList
currentTree = rightLinkingTokenTree; // move to next node of the tree
}
tokenTreeList.add(rootTokenTree.getRightTokenTree());
//End(3)
//Start(4): replace temp with actual values
if(tempTokenTreeList.size() > 0) //there is a open & close round bracket
{
for(TokenTree aTokenTree : tokenTreeList)
{
replaceTempTerminalWithActualValues(aTokenTree,tempTokenTreeList);
}
}
//End(4)
return tokenTreeList;
}

/*
This method replaces each node in TokenTree that is [requireTerminal] and has a temporary value.
The actual value of the node, which may be a tree, is stored in tempTokenTreeList.
*/
private void replaceTempTerminalWithActualValues(TokenTree tokenTree, ArrayList<TokenTree> tempTokenTreeList)
{
Token token = tokenTree.getNodeToken();
if(token != null)
{
if(token.is("requireTerminal") && token.getValue().startsWith("temp_"))
{
int indexPositionInTokenValue = token.getValue().indexOf('_') + 1;
int indexOfTempTokenTreeList = Integer.parseInt(token.getValue().substring(indexPositionInTokenValue));
TokenTree actualValueOfBlock = tempTokenTreeList.get(indexOfTempTokenTreeList);
tokenTree.replaceWith(actualValueOfBlock);
return;// then stop
}
}
if(tokenTree.getRightTokenTree() != null)
{
replaceTempTerminalWithActualValues(tokenTree.getRightTokenTree(),tempTokenTreeList);
}
if(tokenTree.getLeftTokenTree() != null)
{
replaceTempTerminalWithActualValues(tokenTree.getLeftTokenTree(),tempTokenTreeList);
}
}
public void analyzeFeatureModel()
{
if(model.getFeatureModel() != null)
@@ -481,6 +549,17 @@ class TokenTree
boolean isNegated =false;
boolean isOpt = false;
boolean isLinkingOperator = false;

// This method replaces the current TokenTree with a new TokenTree.
public void replaceWith(TokenTree newTokenTree)
{
this.setNodeToken(newTokenTree.getNodeToken());
this.setLeftTokenTree(newTokenTree.getLeftTokenTree());
this.setRightTokenTree(newTokenTree.getRightTokenTree());
this.isLinkingOperator = newTokenTree.getIsLinkingOperator();
this.isOpt = newTokenTree.getIsOpt();
this.isNegated = newTokenTree.getIsNegated();
}
/*
This method selects the the connection operator type based on the type of the (linking) token.
If the type is not specified for the linking node, The default is Required.
@@ -18,9 +18,9 @@ mixsetInlineDefinition- : ( [entityType] [entityName] ( [[mixsetInnerContent]] |

requireStatement : require ( [=subfeature:sub] )? [[requireBody]]

requireBody- : [(([[requireLinkingOptNot]])? [[requireTerminal]] [[requireList]])]
requireBody- : [(([[requireLinkingOptNot]])? (OPEN_ROUND_BRACKET)* [[requireTerminal]] [[requireList]])]

requireList- : ([[requireLinkingOp]] [[requireTerminal]])*
requireList- : ([[requireLinkingOp]] (OPEN_ROUND_BRACKET)* [[requireTerminal]] (CLOSE_ROUND_BRACKET)* )*

requireLinkingOp : ([[requireLinkingOptNot]] | [=and:&|&&|and|,] | [!or:([|][|]?|or|;)] | [=xor:xor|XOR])

@@ -462,4 +462,45 @@ public void parseReqStArgumetToSatisfyFeatureModel_6()
FeatureModel featureModel= model.getFeatureModel();
Assert.assertEquals(false,featureModel.satisfyFeatureModel());
}
@Test
public void parseReqStArgumetToSatisfyFeatureModel_7()
{
UmpleFile umpleFile = new UmpleFile(umpleParserTest.pathToInput,"reqStArgumentParse_validCombinedOpFeatuerModel.ump");
UmpleModel model = new UmpleModel(umpleFile);
model.setShouldGenerate(false);
model.run();
FeatureModel featureModel= model.getFeatureModel();
Assert.assertEquals(true,featureModel.satisfyFeatureModel());
}
@Test
public void parseReqStArgumetToSatisfyFeatureModel_8()
{
UmpleFile umpleFile = new UmpleFile(umpleParserTest.pathToInput,"reqStArgumentParse_validCombinedOpWithRoundBracket.ump");
UmpleModel model = new UmpleModel(umpleFile);
model.setShouldGenerate(false);
model.run();
FeatureModel featureModel= model.getFeatureModel();
Assert.assertEquals(true,featureModel.satisfyFeatureModel());
}
@Test
public void parseReqStArgumetToSatisfyFeatureModel_9()
{
UmpleFile umpleFile = new UmpleFile(umpleParserTest.pathToInput,"reqStArgumentParse_NotvalidCombinedOpWithRoundBracket.ump");
UmpleModel model = new UmpleModel(umpleFile);
model.setShouldGenerate(false);
model.run();
FeatureModel featureModel= model.getFeatureModel();
Assert.assertEquals(false,featureModel.satisfyFeatureModel());
}
@Test
public void parseReqStArgumetToSatisfyFeatureModel_10()
{
UmpleFile umpleFile = new UmpleFile(umpleParserTest.pathToInput,"reqStArgumentParse_validCombinedOpWithComplexRoundBracket.ump");
UmpleModel model = new UmpleModel(umpleFile);
model.setShouldGenerate(false);
model.run();
FeatureModel featureModel= model.getFeatureModel();
Assert.assertEquals(true,featureModel.satisfyFeatureModel());
}

}
@@ -0,0 +1,15 @@

require [ ((A or B) and C ) and (D xor F) ];



mixset A {} mixset B {} mixset C{}
mixset D {}

/*
Not valid configuration of feature model.
*/
//use A;
use C;
use D;

@@ -0,0 +1,12 @@
//This configuration is valid.
require [ A and B or C and D]; // (A and B) or (C and D)
require [B xor C and D]; // B xor (C and D)
require [1..2 of {C,D} not E];

mixset A {} mixset B {} mixset C{} mixset D {} mixset E{}

use C;
use D;



@@ -0,0 +1,15 @@
require [ ((A xor B) and C ) and (D xor (2..3 of {A,B,C,D} and E) ) ];



mixset A {} mixset B {} mixset C{}
mixset D {} mixset E{}

/*
valid configuration of feature model.
*/

use A;
use C;
use E;

@@ -0,0 +1,16 @@

require [ ((A or B) and C ) and (D xor F) ];



mixset A {} mixset B {} mixset C{}
mixset D {}

/*
use-statements result in a valid configuration of feature model.
*/

use A;
use C;
use D;

0 comments on commit bc0cac3

Please sign in to comment.