/* 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.configuration; import java.util.ArrayList; import java.util.HashSet; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.prop4j.And; import org.prop4j.Literal; import org.prop4j.Node; import org.prop4j.SatSolver; import org.sat4j.specs.TimeoutException; import featureide.fm.core.Feature; import featureide.fm.core.FeatureModel; import featureide.fm.core.editing.NodeCreator; public class Configuration { private static final int TIMEOUT = 1000; private SelectableFeature root; private ArrayList<SelectableFeature> features = new ArrayList<SelectableFeature>(); private Hashtable<String, SelectableFeature> table = new Hashtable<String, SelectableFeature>(); private Node rootNode; private boolean propagate; private FeatureModel featureModel; public Configuration(FeatureModel featureModel) { this(featureModel, true); } public Configuration(FeatureModel featureModel, boolean propagate) { this.featureModel = featureModel; this.propagate = propagate; root = new SelectableFeature(this, featureModel.getRoot()); initFeatures(root, featureModel.getRoot()); rootNode = NodeCreator.createNodes(featureModel); rootNode = rootNode.toCNF(); updateAutomaticValues(); } private void initFeatures(SelectableFeature sFeature, Feature feature) { features.add(sFeature); table.put(sFeature.getName(), sFeature); for (Feature child : feature.getChildren()) { SelectableFeature sChild = new SelectableFeature(this, child); sFeature.addChild(sChild); initFeatures(sChild, child); } } /** * Checks that all manual and automatical selections are valid. * * @return */ public boolean valid() { LinkedList<Node> children = new LinkedList<Node>(); for (SelectableFeature feature : features) { Literal literal = new Literal(feature.getName()); literal.positive = feature.getSelection() == Selection.SELECTED; children.add(literal); } try { return new SatSolver(rootNode, TIMEOUT).isSatisfiable(children); } catch (TimeoutException e) { } return false; } /** * Checks that all manual selections are valid. This method returns the same * result as <code>valid()</code> in non-propagate mode except the case when * a feature is to be selected/deselected that is not possible due to an * automatically determined selection in propagate mode. In this case you * get an exception on calling <code>setManuall()</code> and this method * returns <code>true</code> since the selection/deselection is ignored. * * @return */ public boolean validManually() { LinkedList<Node> children = new LinkedList<Node>(); for (SelectableFeature feature : features) if (feature.getFeature().isLayer()) { Literal literal = new Literal(feature.getName()); literal.positive = feature.getManual() == Selection.SELECTED; children.add(literal); } try { return new SatSolver(rootNode, TIMEOUT).isSatisfiable(children); } catch (TimeoutException e) { } return false; } public long number() { LinkedList<Node> children = new LinkedList<Node>(); for (SelectableFeature feature : features) if (!feature.hasChildren()) { if (feature.getSelection() == Selection.SELECTED) children.add(new Literal(feature.getName(), true)); if (feature.getSelection() == Selection.UNSELECTED) children.add(new Literal(feature.getName(), false)); } Node node = new And(rootNode.clone(), new And(children)); return new SatSolver(node, 250).countSolutions(); } private void updateAutomaticValues() { if (!propagate) return; resetAutomaticValues(); updateManualDefinedValues(); updateManualUndefinedValues(); } private void resetAutomaticValues() { for (SelectableFeature feature : features) feature.setAutomatic(Selection.UNDEFINED); } private void updateManualDefinedValues() { List<Node> literals = new LinkedList<Node>(); for (SelectableFeature feature : features) if (feature.getManual() != Selection.UNDEFINED) { Literal literal = new Literal(feature.getName()); literal.positive = feature.getManual() == Selection.SELECTED; literals.add(literal); } SatSolver solver = new SatSolver(rootNode.clone(), TIMEOUT); for (Node node : literals) { Literal literal = (Literal) node; literal.positive = !literal.positive; try { if (!solver.isSatisfiable(literals)) { SelectableFeature feature = table.get(literal.var); feature.setAutomatic(feature.getManual()); } } catch (TimeoutException e) { e.printStackTrace(); } literal.positive = !literal.positive; } } private void updateManualUndefinedValues() { List<Node> children = new LinkedList<Node>(); for (SelectableFeature feature : features) if (feature.getManual() != Selection.UNDEFINED) { Literal literal = new Literal(feature.getName()); literal.positive = feature.getManual() == Selection.SELECTED; children.add(literal); } Node node = new And(rootNode.clone(), new And(children)); SatSolver solver = new SatSolver(node, TIMEOUT); for (Literal literal : solver.knownValues()) { SelectableFeature feature = table.get(literal.var); if (feature.getManual() == Selection.UNDEFINED) feature.setAutomatic(literal.positive ? Selection.SELECTED : Selection.UNSELECTED); } } public void setManual(SelectableFeature feature, Selection selection) { feature.setManual(selection); updateAutomaticValues(); } public void setManual(String name, Selection selection) { SelectableFeature feature = table.get(name); if (feature == null) throw new FeatureNotFoundException(); setManual(feature, selection); } public SelectableFeature getRoot() { return root; } public void resetValues() { for (SelectableFeature feature : features) feature.setManual(Selection.UNDEFINED); updateAutomaticValues(); } public Set<Feature> getUnSelectedFeatures() { HashSet<Feature> result = new HashSet<Feature>(); findUnSelectedFeatures(getRoot(), result); return result; } private void findUnSelectedFeatures(SelectableFeature sf, HashSet<Feature> result) { if (sf.getSelection() == Selection.UNSELECTED) result.add(sf.getFeature()); for (TreeElement child : sf.getChildren()) findUnSelectedFeatures((SelectableFeature) child, result); } public Set<Feature> getSelectedFeatures() { HashSet<Feature> result = new HashSet<Feature>(); findSelectedFeatures(getRoot(), result); return result; } private void findSelectedFeatures(SelectableFeature sf, HashSet<Feature> result) { if (sf.getSelection() == Selection.SELECTED) result.add(sf.getFeature()); for (TreeElement child : sf.getChildren()) findSelectedFeatures((SelectableFeature) child, result); } public FeatureModel getFeatureModel() { return featureModel; } }