package splar.core.util.alloy;
import java.util.Iterator;
import java.util.LinkedList;
import splar.core.fm.FeatureGroup;
import splar.core.fm.FeatureModel;
import splar.core.fm.FeatureTreeNode;
import splar.core.fm.SolitaireFeature;
public class FM2AlloyConversor {
private static String header =
"\r\n//Header Declarations\r\n\n" +
"sig FM {" + "\r\n" +
" features: set Name" + "\r\n" +
"}" + "\r\n" +
"sig Name {}" + "\r\n" +
"pred optional[A,B:Name, conf:set Name] {" + "\r\n" +
" B in conf => A in conf" + "\r\n" +
"}" + "\r\n" +
"pred mandatory[A,B:Name, conf:set Name] {" + "\r\n" +
" A in conf <=> B in conf" + "\r\n" +
"}" + "\r\n" +
"pred root[A:Name, conf: set Name] {" + "\r\n" +
" A in conf" + "\r\n" +
"}" + "\r\n" +
"pred orFeature[A,B,C:Name, conf: set Name] {" + "\r\n" +
" A in conf <=> (B in conf) or (C in conf)" + "\r\n" +
"}" + "\r\n" +
"pred alternativeFeature[A,B,C:Name, conf: set Name] {" + "\r\n" +
" orFeature[A,B,C,conf]" + "\r\n" +
" B in conf => C !in conf" + "\r\n" +
" C in conf => B !in conf" + "\r\n" +
"}" + "\r\n";
public FM2AlloyConversor() {}
public static String convert(FeatureModel fm, boolean includeHeader) {
String alloy = "";
if ( includeHeader ) {
alloy += header;
}
// declares feature model
alloy +=
"\r\n\n// Feature Model Declaration\r\n\r\n" +
"one sig " + fm.getName() + " extends FM {}\r\n";
// declare features
alloy += "one sig ";
for( Iterator<FeatureTreeNode> it = fm.getNodes().iterator() ; it.hasNext() ; ) {
FeatureTreeNode feature = it.next();
if ( !(feature instanceof FeatureGroup) ) {
alloy += feature.getID();
if ( it.hasNext() ) {
alloy += ",";
}
}
}
alloy += " extends Name {}\r\n";
// add features to the feature model
alloy += "fact " +fm.getName() + "Features {\r\n" +
"\t" + fm.getName() + ".features = ";
for( Iterator<FeatureTreeNode> it = fm.getNodes().iterator() ; it.hasNext() ; ) {
FeatureTreeNode feature = it.next();
if ( !(feature instanceof FeatureGroup) ) {
alloy += feature.getID();
if ( it.hasNext() ) {
alloy += "+";
}
}
}
alloy += "\r\n}\r\n";
// add feature model semantics: constraints
alloy += "pred semantics" + fm.getName() + "[conf: set Name] {\r\n" +
" conf in " + fm.getName() + ".features\r\n";
// root node
alloy += " root[" + fm.getRoot().getID() + ",conf]\r\n";
// feature tree constraints
alloy += FT2Alloy(fm);
// extra constraints
alloy += "}\r\n";
return alloy;
}
private static String FT2Alloy(FeatureModel fm) {
String output = "";
LinkedList<FeatureTreeNode> features = new LinkedList<FeatureTreeNode>();
features.add(fm.getRoot());
while( !features.isEmpty() ) {
FeatureTreeNode feature = features.getFirst();
features.removeFirst();
for( int i = 0 ; i < feature.getChildCount() ; i++ ) {
FeatureTreeNode childFeature = (FeatureTreeNode)feature.getChildAt(i);
if ( childFeature instanceof SolitaireFeature ) {
// optional?
if ( ((SolitaireFeature)childFeature).isOptional() ) {
output += " optional[" + feature.getID() + "," + childFeature.getID() +",conf]\r\n";
}
// mandatory
else {
output += " mandatory[" + feature.getID() + "," + childFeature.getID() +",conf]\r\n";
}
features.add(childFeature);
}
else if ( childFeature instanceof FeatureGroup ) {
FeatureGroup group = (FeatureGroup)childFeature;
FeatureTreeNode parent = (FeatureTreeNode)group.getParent();
// [1] - exclusive-OR
if ( group.getMax() == 1 && group.getMax() == 1 ) {
output += " alternativeFeature[" + parent.getID() +",";
}
// [1,*] - inclusive-OR
else if ( group.getMax() == -1 || group.getMax() == group.getChildCount() ) {
output += " orFeature[" + parent.getID() +",";
}
int childCount = group.getChildCount() ;
for( int j = 0 ; j < childCount ; j++ ) {
FeatureTreeNode groupedFeature = (FeatureTreeNode)group.getChildAt(j);
output += groupedFeature.getID();
output += ",";
features.add(groupedFeature);
// for( int k = 0 ; k < groupedFeature.getChildCount() ; k++ ) {
// features.add((FeatureTreeNode)groupedFeature.getChildAt(k));
// }
}
output += "conf]\r\n";
}
}
}
return output;
}
}