/* FeatureIDE - An IDE to support feature-oriented software development
* Copyright (C) 2005-2009 FeatureIDE Team, University of Magdeburg
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* See http://www.fosd.de/featureide/ for further information.
*/
package featureide.fm.core.editing.evaluation;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import org.prop4j.And;
import org.prop4j.AtMost;
import org.prop4j.Equals;
import org.prop4j.Implies;
import org.prop4j.Literal;
import org.prop4j.Node;
import org.prop4j.Not;
import org.prop4j.Or;
import org.sat4j.specs.TimeoutException;
import featureide.fm.core.Feature;
import featureide.fm.core.FeatureModel;
public abstract class Generator {
public static final int TIMEOUT = 60;
public final static int maxChildren = 10;
public final static int minLiterals = 2;
public final static int maxLiterals = 5;
public static FeatureModel generateFeatureModel(long id, int numberOfFeatures) {
Random random = new Random(id);
FeatureModel fm = generateFeatureDiagram(random, numberOfFeatures);
generateConstraints(fm, random, numberOfFeatures / 10);
return fm;
}
public static FeatureModel generateFeatureDiagram(Random random, int numberOfFeatures) {
FeatureModel fm = new FeatureModel();
List<Feature> leaves = new LinkedList<Feature>();
leaves.add(fm.getFeature("C1"));
int count = 1;
while (count < numberOfFeatures) {
int parentIndex = random.nextInt(leaves.size());
Feature parent = leaves.remove(parentIndex);
fm.renameFeature(parent.getName(), "A" + parent.getName().substring(1));
int childrenCount = random.nextInt(maxChildren) + 1;
childrenCount = Math.min(childrenCount, numberOfFeatures - count);
for (int i = 1; i <= childrenCount; i++) {
Feature child = new Feature(fm, "C" + (count + i));
fm.addFeature(child);
parent.addChild(child);
leaves.add(child);
}
if (random.nextBoolean()) {
parent.changeToAnd();
for (Feature child : parent.getChildren())
child.setMandatory(random.nextBoolean());
}
else if (random.nextBoolean())
parent.changeToOr();
count += childrenCount;
}
fm.performRenamings();
return fm;
}
public static void generateConstraints(FeatureModel fm, Random random, int numberOfConstraints) {
boolean valid = true;
try {
valid = fm.isValid();
if (!valid)
System.err.println("Feature model not valid!");
} catch (TimeoutException e) {
e.printStackTrace();
}
Object[] names = fm.getOldFeatureNames().toArray();
int k = 0;
for (int i = 0; i < numberOfConstraints;) {
Node node = getRandomLiteral(names, random);
for (int j = random.nextInt(maxLiterals - minLiterals + 1) + minLiterals; j > 1; j--) {
if (random.nextBoolean()) {
if (random.nextBoolean())
node = new And(node, getRandomLiteral(names, random));
else
node = new Or(node, getRandomLiteral(names, random));
}
else {
if (random.nextBoolean())
node = new Implies(node, getRandomLiteral(names, random));
else
node = new Equals(node, getRandomLiteral(names, random));
}
if (random.nextBoolean() && random.nextBoolean())
node = new Not(node);
}
fm.addPropositionalNode(node);
try {
if (!valid || fm.isValid()) {
i++;
System.out.println("E\t" + i + "\t" + node);
}
else {
fm.removePropositionalNode(node);
System.err.println("F\t" + ++k + "\t" + node);
}
} catch (TimeoutException e) {
fm.removePropositionalNode(node);
}
}
}
public static FeatureModel refactoring(FeatureModel originalFM, long id, int numberOfEdits) {
FeatureModel fm = (FeatureModel) originalFM.clone();
Random random = new Random(id);
for (int i = 0; i < numberOfEdits; i++) {
List<Feature> list = new LinkedList<Feature>(fm.getFeatures());
List<Feature> randomizedList = new LinkedList<Feature>();
while (!list.isEmpty())
randomizedList.add(list.remove(random.nextInt(list.size())));
int r = random.nextInt(3);
if (r == 0) {
//Alternative to Or+Constraint
for (Feature feature : randomizedList)
if (feature.getChildrenCount() > 1 && feature.isAlternative()) {
feature.changeToOr();
LinkedList<Node> nodes = new LinkedList<Node>();
for (Feature child : feature.getChildren())
nodes.add(new Literal(child.getName()));
fm.addPropositionalNode(new AtMost(1,nodes).toCNF());
break;
}
}
else if (r == 1) {
//Mandatory to Optional+Constraint
for (Feature feature : randomizedList) {
Feature parent = feature.getParent();
if (parent != null && parent.isAnd() && !parent.isFirstChild(feature) && feature.isMandatory()) {
feature.setMandatory(false);
fm.addPropositionalNode(new Implies(new Literal(parent.getName()),new Literal(feature.getName())));
break;
}
}
}
else {
//move feature to parent's parent
for (Feature child : randomizedList) {
Feature feature = child.getParent();
if (feature != null && feature.isMandatory() && feature.isAnd() && !feature.isFirstChild(child)) {
Feature parent = feature.getParent();
if (parent != null && parent.isAnd()) {
feature.removeChild(child);
parent.addChild(child);
break;
}
}
}
}
}
return fm;
}
public static FeatureModel generalization(FeatureModel originalFM, long id, int numberOfEdits) {
FeatureModel fm = (FeatureModel) originalFM.clone();
Random random = new Random(id);
for (int i = 0; i < numberOfEdits; i++) {
List<Feature> list = new LinkedList<Feature>(fm.getFeatures());
List<Feature> randomizedList = new LinkedList<Feature>();
while (!list.isEmpty())
randomizedList.add(list.remove(random.nextInt(list.size())));
int r = 1 + random.nextInt(9);
if (r == 1) {
//Alternative to Or
for (Feature feature : randomizedList)
if (feature.getChildrenCount() > 1 && feature.isAlternative()) {
feature.changeToOr();
break;
}
}
else if (r == 2) {
//move Optional into Or
r2:
for (Feature feature : randomizedList) {
Feature parent = feature.getParent();
if (parent != null && parent.isAnd() && feature.isMandatory() && feature.isOr()) {
for (Feature child : parent.getChildren())
if (!child.isMandatory()) {
parent.removeChild(child);
feature.addChild(child);
break r2;
}
}
}
}
else if (r == 3) {
//And to Or
for (Feature feature : randomizedList)
if (feature.getChildrenCount() > 1 && feature.isAnd()) {
feature.changeToOr();
break;
}
}
else if (r == 4) {
//new feature in Alternative
for (Feature feature : randomizedList)
if (feature.hasChildren() && feature.isAlternative()) {
int j = 1;
Feature child;
do {
child = new Feature(fm, "C" + j++);
} while (!fm.addFeature(child));
feature.addChild(child);
break;
}
}
else if (r == 5) {
//Or to And
for (Feature feature : randomizedList)
if (feature.getChildrenCount() > 1 && feature.isOr()) {
Feature parent = feature.getParent();
if (parent != null && !parent.isFirstChild(feature) && parent.isAnd()) {
parent.removeChild(feature);
for (Feature child : feature.getChildren()) {
parent.addChild(child);
child.setMandatory(false);
}
break;
}
}
}
else if (r == 6) {
//Mandatory to Optional
for (Feature feature : randomizedList) {
Feature parent = feature.getParent();
if (parent != null && parent.isAnd() && !parent.isFirstChild(feature) && feature.isMandatory()) {
feature.setMandatory(false);
fm.addPropositionalNode(new Implies(new Literal(parent.getName()),new Literal(feature.getName())));
break;
}
}
}
else if (r == 7) {
//Alternative to And
for (Feature feature : randomizedList)
if (feature.getChildrenCount() > 1 && feature.isAlternative()) {
Feature parent = feature.getParent();
if (parent != null && !parent.isFirstChild(feature) && parent.isAnd()) {
parent.removeChild(feature);
for (Feature child : feature.getChildren()) {
parent.addChild(child);
child.setMandatory(false);
}
break;
}
}
}
else if (r == 8) {
//new Optional in And
for (Feature feature : randomizedList)
if (feature.hasChildren() && feature.isAnd()) {
int j = 1;
Feature child;
do {
child = new Feature(fm, "C" + j++);
} while (!fm.addFeature(child));
child.setMandatory(false);
feature.addChild(child);
break;
}
}
else {
//remove Constraint
List<Node> nodes = fm.getPropositionalNodes();
if (!nodes.isEmpty()) {
int index = random.nextInt(nodes.size());
fm.removePropositionalNode(nodes.get(index));
}
}
}
return fm;
}
public static FeatureModel arbitraryEdits(FeatureModel originalFM, long id, int numberOfEdits) {
boolean valid = false;
try {
valid = originalFM.isValid();
} catch (TimeoutException e) {
e.printStackTrace();
}
FeatureModel fm = (FeatureModel) originalFM.clone();
Random random = new Random(id);
for (int i = 0; i < numberOfEdits; i++) {
FeatureModel backup = valid ? fm.clone() : null;
List<Feature> list = new LinkedList<Feature>(fm.getFeatures());
List<Feature> randomizedList = new LinkedList<Feature>();
while (!list.isEmpty())
randomizedList.add(list.remove(random.nextInt(list.size())));
int r = 1 + random.nextInt(5);
if (r == 1) {
//delete or add feature
if (random.nextBoolean())
for (Feature feature : randomizedList) {
Feature parent = feature.getParent();
if (!feature.hasChildren() && parent != null && !parent.isFirstChild(feature)) {
fm.deleteFeature(feature);
break;
}
}
else
for (Feature feature : randomizedList)
if (feature.hasChildren()) {
int j = 1;
Feature child;
do {
child = new Feature(fm, "C" + j++);
} while (!fm.addFeature(child));
feature.addChild(child);
break;
}
}
else if (r == 2) {
//alter group type
for (Feature feature : randomizedList) {
if (feature.hasChildren()) {
if (feature.isAlternative())
if (random.nextBoolean())
feature.changeToAnd();
else
feature.changeToOr();
else if (feature.isAnd())
if (random.nextBoolean())
feature.changeToAlternative();
else
feature.changeToOr();
else
if (random.nextBoolean())
feature.changeToAnd();
else
feature.changeToAlternative();
break;
}
}
}
else if (r == 3) {
//change mandatory/optional
for (Feature feature : randomizedList) {
Feature parent = feature.getParent();
if (parent != null && parent.isAnd() && !parent.isFirstChild(feature)) {
feature.setMandatory(!feature.isMandatory());
break;
}
}
}
else if (r == 4) {
//move a concrete feature to another branch
for (Feature feature : randomizedList) {
Feature parent = feature.getParent();
if (!feature.hasChildren() && parent != null && !parent.isFirstChild(feature)) {
parent.removeChild(feature);
Feature newParent = parent;
for (Feature compound : randomizedList)
if (compound != parent && compound.hasChildren())
newParent = compound;
newParent.addChild(feature);
break;
}
}
}
else {
//delete or add constraint
if (fm.getPropositionalNodes().size() > 0 && random.nextBoolean()) {
int index = random.nextInt(fm.getPropositionalNodes().size());
fm.removePropositionalNode(index);
}
else
generateConstraints(fm, random, 1);
}
try {
if (valid && !fm.isValid()) {
System.out.println("Void feature model by arbitrary edit " + r);
fm = backup;
i--;
}
} catch (TimeoutException e) {
e.printStackTrace();
}
}
return fm;
}
private static Literal getRandomLiteral(Object[] names, Random random) {
int index = random.nextInt(names.length);
return new Literal(names[index], random.nextBoolean());
}
public static String getTimeString(long dur) {
if (dur < 1000000)
return dur + "nsec";
dur /= 1000000;
if (dur < 1000)
return dur + "msec";
dur /= 1000;
if (dur < 60)
return dur + "sec";
dur /= 60;
if (dur < 60)
return dur + "min";
dur /= 60;
return dur + "h";
}
}