package splar.apps;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.javabdd.BDD;
import net.sf.javabdd.BDDFactory;
import net.sf.javabdd.JFactory;
import splar.plugins.configuration.bdd.javabdd.BDDConfigurationEngine;
import au.com.bytecode.opencsv.CSVReader;
public class SPLOTCatalogConfigurationBackEnd {
String productCatalogFilePath = "c:\\users\\marcilio\\eclipse\\splot-project\\webcontent\\models\\catalog\\dell_laptops_usa_fm_catalog.csv";
int nodeNum = 100000;
int cacheSize = 100000;
public static void main(String args[]) {
new SPLOTCatalogConfigurationBackEnd().generateAndSaveInteractiveConfigurationBDD();
// new SPLOTCatalogConfigurationBackEnd().extractUniqueProductAttributes();
// new SPLOTCatalogConfigurationBackEnd().generateCrossTreeConstraintAsCNFClauses();
}
/*
* Use this method to create and save (offline) a BDD for a given (feature model + catalog of products)
* The saved BDD is loaded by the configuration engine (online) which saves a huge time since it doesn't need to build the BDD again
*/
// command line: java -mx512m -cp ..\resources\lib\javabdd.jar;..\resources\lib\opencsv-1.8.jar;.\ startup.SPLOTCatalogConfigurationBackEnd
public void generateAndSaveInteractiveConfigurationBDD() {
try {
String path = "c:\\users\\marcilio\\eclipse\\splot-project\\webcontent\\models\\catalog\\";
String fileName = "dell_laptops_usa_fm";
// String fileName = "trekbikes_fm";
// String fileName = "simple_bike_fm";
String featureModelPath = "file:///" + path + fileName + ".xml";
String catalogPath = path + fileName + "_catalog.csv";
BDDConfigurationEngine confEngine = new BDDConfigurationEngine(featureModelPath, 1000000, 1000000);
confEngine.addProductCatalog(catalogPath);
confEngine.reset();
confEngine.getReasoner().saveToFile(path, fileName);
} catch (Exception e) {
e.printStackTrace();
}
}
public void extractUniqueProductAttributes() {
try {
CSVReader reader = new CSVReader(new FileReader(productCatalogFilePath));
String [] header = reader.readNext();
boolean[] optional = new boolean[header.length];
Arrays.fill(optional, false);
List<Set<String>> uniqueColumnValues = new ArrayList<Set<String>>(header.length);
for( int i = 0 ; i < header.length ; i++ ) {
uniqueColumnValues.add(i, new LinkedHashSet<String>());
}
String [] nextLine = reader.readNext();
int line = 0;
while ( nextLine != null) {
for( int i = 0 ; i < header.length ; i++ ) {
String value = nextLine[i].trim();
if ( value.length() > 0 ) {
uniqueColumnValues.get(i).add(value);
if ( value.indexOf('(') != -1 || value.indexOf(')') != -1 ) {
throw new Exception("Value contains parantheses (column: " + header[i] + " /line: " + line + " ): " + value );
}
}
else {
if (!optional[i] ) {
optional[i] = true;
System.out.println("Optional Feature: " + header[i] );
}
}
}
nextLine = reader.readNext();
line++;
}
reader.close();
for( int i = 0 ; i < header.length ; i++ ) {
if ( i >=2 && !header[i].startsWith("$" ) ) {
System.out.println(header[i] + "----------------------------------------");
String uniqueValues [] = uniqueColumnValues.get(i).toArray(new String[0]);
Collections.sort(Arrays.asList(uniqueValues));
System.out.println("\t\t\t" + (optional[i] ? ":o " : ":m ") + header[i]);
for( String value : uniqueValues ) {
System.out.println("\t\t\t\t: " + value + " (" + genID(header[i],value) + ")");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String genID(String header, String name) {
// name = name.trim();
String lex = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
for( int i = 0 ; i < name.length() ; i++ ) {
if (lex.indexOf(name.charAt(i)) == -1 ) {
name = name.replace(name.charAt(i), '_');
}
}
return ("_" + header + "_" + name).toLowerCase();
}
public List<String> toListOfIDs(String header[], String values[]) {
List<String> ids = new ArrayList(values.length);
for( int i = 0 ; i < values.length ; i++ ) {
ids.add(i, genID(header[i], values[i]));
}
return ids;
}
public void generateCrossTreeConstraintAsCNFClauses() {
try {
// Each list in the catalog contains a list of string representing the features in the product
List<List<String>> catalog = new LinkedList<List<String>>();
CSVReader reader = new CSVReader(new FileReader(productCatalogFilePath));
String [] header = reader.readNext();
String [] nextLine = reader.readNext();
while ( nextLine != null) {
catalog.add(toListOfIDs(header, nextLine));
nextLine = reader.readNext();
}
reader.close();
/*********************************************************************************************************
* compute number of variables by counting distinct catalog items
*********************************************************************************************************/
List<String> varIndex2NameMap = new LinkedList<String>();
Map<String,Integer> varName2IndexMap = new HashMap<String, Integer>();
for( List<String> product : catalog ) {
for( String item : product ) {
if ( !varName2IndexMap.containsKey(item) ) {
varIndex2NameMap.add(item);
varName2IndexMap.put(item,0);
}
}
}
/*********************************************************************************************************
* defines BDD variable order
*********************************************************************************************************/
Collections.shuffle(varIndex2NameMap);
int countVars = 0;
for( String var : varIndex2NameMap ) {
varName2IndexMap.put(var,countVars++);
}
System.out.println("\nVARIABLES ORDER/INDEXES -------------------");
List<String> sortedVars = new ArrayList(varIndex2NameMap);
Collections.sort(sortedVars);
for( String var : sortedVars) {
System.out.println(varName2IndexMap.get(var) + ": " + var );
}
/*********************************************************************************************************
* builds the BDD for product catalog
*********************************************************************************************************/
// (a or ~b) and c
BDDFactory bddFactory = JFactory.init(nodeNum, cacheSize);
bddFactory.setVarNum(countVars);
BDD fbdd = bddFactory.zero();
for( List<String> product : catalog ) {
BDD tempBDD = bddFactory.one();
Set<String> productSet = new HashSet<String>(product);
for( String distinctItem : varIndex2NameMap ) {
if ( productSet.contains(distinctItem) ) {
tempBDD.andWith(bddFactory.ithVar(varName2IndexMap.get(distinctItem)).id());
}
else {
tempBDD.andWith(bddFactory.nithVar(varName2IndexMap.get(distinctItem)).id());
}
}
fbdd.orWith(tempBDD);
}
/*********************************************************************************************************
* prints BDD dot file
*********************************************************************************************************/
try {
// PrintStream savedOut = System.out;
// System.setOut(new PrintStream(new FileOutputStream("c:\\users\\marcilio\\temp\\bdd.dot")));
// fbdd.printDot();
// System.setOut(savedOut);
}
catch (Exception e) {
}
/*********************************************************************************************************
* extracts CNF from negated BDD
*********************************************************************************************************/
System.out.println("\nCNF FORMULA -------------------");
int formulaIndex = 1;
// for( BDD.AllSatIterator it = new BDD.AllSatIterator(fbdd.not()) ; it.hasNext() ; ) {
// byte[] path = it.nextSat();
// int index = 0;
// System.out.print(formulaIndex++ + ": (");
// for( byte value : path ) {
// String var = varIndex2NameMap.get(index);
// if ( value == 0 ) {
// if ( index != 0 ) System.out.print(",");
// System.out.print(var);
// }
// else if ( value == 1 ) {
// if ( index != 0 ) System.out.print(",");
// System.out.print("~"+var);
// }
// index++;
// }
// System.out.println(")");
// }
/*********************************************************************************************************
* enumerates BDD solutions that must match catalog products
*********************************************************************************************************/
System.out.println("\nPRODUCTS FROM BDD -------------------");
System.out.println("BDD size: " + fbdd.nodeCount());
System.out.println("Solutions: " + fbdd.satCount());
System.out.println("Path count: " + fbdd.not().pathCount());
List<String> product = new LinkedList<String>();
int countProducts = 0;
for( BDD.AllSatIterator it = new BDD.AllSatIterator(fbdd) ; it.hasNext() ; ) {
product.clear();
byte[] path = it.nextSat();
int index = 0;
for( byte value : path ) {
String var = varIndex2NameMap.get(index);
if ( value == 1 ) {
product.add(var);
}
index++;
}
Collections.sort(product);
// System.out.print("\r\n" + (++countProducts) + ": ");
// for( String item : product ) {
// System.out.print(item + ",");
// }
}
} catch (Exception e) {
// TODO: handle exception
}
}
}