/* 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.io.waterloo;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Scanner;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.prop4j.NodeWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import featureide.fm.core.Feature;
import featureide.fm.core.FeatureModel;
import featureide.fm.core.io.AbstractFeatureModelWriter;
/**
* TODO description
*
* @author Fabian Wielgorz
*/
public class WaterlooWriter extends AbstractFeatureModelWriter {
public WaterlooWriter() {
}
/**
* Creates a new writer and sets the feature model to write out.
*
* @param featureModel the structure to write
*/
public WaterlooWriter(FeatureModel featureModel) {
setFeatureModel(featureModel);
}
@Override
public String writeToString() {
//Create Empty DOM Document
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setIgnoringComments(true);
dbf.setIgnoringElementContentWhitespace(false);
dbf.setCoalescing(false);
dbf.setExpandEntityReferences(false);
DocumentBuilder db = null;
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException pce) {
System.err.println(pce);
System.exit(1);
}
Document doc = db.newDocument();
//Create the Xml Representation
createXmlDoc(doc);
//Transform the Xml Representation into a String
Transformer transfo = null;
try {
transfo = TransformerFactory.newInstance().newTransformer();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerFactoryConfigurationError e) {
e.printStackTrace();
}
transfo.setOutputProperty(OutputKeys.METHOD, "xml");
transfo.setOutputProperty(OutputKeys.INDENT, "yes");
transfo.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(doc);
try {
transfo.transform(source, result);
} catch (TransformerException e) {
e.printStackTrace();
}
return result.getWriter().toString();
}
/**
* Creates the DOM Document Representation from the feature model fmodel
* by using createXmlDocRec
* @param doc Document where the feature model is put
*/
private void createXmlDoc(Document doc) {
Element elem = doc.createElement("feature_model");
elem.setAttribute("name", "FeatureIDE model");
doc.appendChild(elem);
Node featTree = doc.createElement("feature_tree");
elem.appendChild(featTree);
featTree.appendChild(doc.createTextNode("\n"));
createXmlDocRec(doc, featTree, featureModel.getRoot(), false, "");
createPropositionalConstraints(doc, elem);
}
/**
* Creates the DOM Document Representation from the feature model fmodel
* by recursively building the Nodes
* @param doc Document where the feature model is put
* @param nod Current Node in the Document Tree
* @param feat Current Feature in the feature model Tree
* @param andMode true if the connection between the current feature and
* its parent is of the type "and", false otherwise
* @param indent indentation of the parent feature
*/
private void createXmlDocRec(Document doc, Node nod, Feature feat,
Boolean andMode, String indent) {
String newIndent;
Node textNode;
LinkedList<Feature> children;
Boolean nextAndMode = false;
if (feat == null) return;
String fName = feat.getName();
if (feat.isRoot()) {
textNode = doc.createTextNode(":r " + fName + "(" + fName + ")\n");
newIndent = "\t";
} else if (andMode) {
if (feat.isMandatory()) {
textNode = doc.createTextNode(indent + ":m " + fName + "(" +
fName + ")\n") ;
} else {
textNode = doc.createTextNode(indent + ":o " + fName + "(" +
fName + ")\n") ;
}
} else {
textNode = doc.createTextNode(indent + ": " + fName + "(" +
fName + ")\n");
}
nod.appendChild(textNode);
children = feat.getChildren();
if (children.isEmpty()) return;
if (feat.isAnd()) {
nextAndMode = true;
newIndent = indent + "\t";
} else if (feat.isOr()) {
textNode = doc.createTextNode(indent + "\t:g [1,*]\n");
nod.appendChild(textNode);
newIndent = indent + "\t\t";
nextAndMode = false;
} else if (feat.isAlternative()) {
textNode = doc.createTextNode(indent + "\t:g [1,1]\n");
nod.appendChild(textNode);
newIndent = indent + "\t\t";
nextAndMode = false;
} else throw new IllegalStateException ("Can't determine " +
"Connectiontype of Rootfeature");
Iterator<Feature> i = children.iterator();
while (i.hasNext()) {
createXmlDocRec(doc, nod , i.next(), nextAndMode, newIndent);
}
}
/**
* Inserts the tags concerning propositional constraints into the DOM
* document representation
* @param doc
* @param FeatMod Parent node for the propositonal nodes
*/
private void createPropositionalConstraints(Document doc, Node FeatMod) {
if (featureModel.getPropositionalNodes().isEmpty())
return;
Node propConstr = doc.createElement("constraints");
Node newNode;
FeatMod.appendChild(propConstr);
newNode = doc.createTextNode("\n");
propConstr.appendChild(newNode);
int i = 1;
for (org.prop4j.Node node : featureModel.getPropositionalNodes()) {
String[] symbols = new String[] {"~", " and ", " or ",
" imp ", " biimp ", ", ", "choose", "atleast", "atmost"};
String nodeString = NodeWriter.nodeToString(node, symbols, true);
if ((nodeString.startsWith("(")) && (nodeString.endsWith(")"))) {
nodeString = nodeString.substring(1, nodeString.length() - 1);
}
nodeString = addBrackets(nodeString);
newNode = doc.createTextNode("C" + i + ":" + nodeString + "\n");
propConstr.appendChild(newNode);
i++;
}
}
/**
* Adding Brackets around negated Literals
* @param original String describing propositional constraint
* @return
*/
private String addBrackets (String original) {
StringBuffer result = new StringBuffer();
String newLine = original.replace("(", " ( ");
newLine = newLine.replace(")", " ) ");
newLine = newLine.replace("~", " ~ ");
Scanner scan = new Scanner(newLine);
while (scan.hasNext()) {
String token = scan.next();
if ((token.equals("~")) && scan.hasNext()) {
String token2 = scan.next();
if (!token2.equals("(")) {
result.append("( ~");
result.append(token2);
result.append(" )");
} else {
result.append("( ~(");
}
} else {
result.append(token);
}
result.append(" ");
}
return result.toString();
}
}