/**
* Copyright (c) 2011 Michael Kutschke.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Michael Kutschke - initial API and implementation.
*/
package org.eclipse.recommenders.jayes.io.xmlbif;
import static org.eclipse.recommenders.jayes.io.xmlbif.Constants.DEFINITION;
import static org.eclipse.recommenders.jayes.io.xmlbif.Constants.FOR;
import static org.eclipse.recommenders.jayes.io.xmlbif.Constants.GIVEN;
import static org.eclipse.recommenders.jayes.io.xmlbif.Constants.NAME;
import static org.eclipse.recommenders.jayes.io.xmlbif.Constants.OUTCOME;
import static org.eclipse.recommenders.jayes.io.xmlbif.Constants.TABLE;
import static org.eclipse.recommenders.jayes.io.xmlbif.Constants.VARIABLE;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.apache.commons.lang3.StringEscapeUtils;
import org.eclipse.recommenders.internal.jayes.io.util.XMLUtil;
import org.eclipse.recommenders.jayes.BayesNet;
import org.eclipse.recommenders.jayes.BayesNode;
import org.eclipse.recommenders.jayes.io.IBayesNetWriter;
/**
* a Writer thats writes the XMLBIF v0.3 format (<a href="http://www.cs.cmu.edu/~fgcozman/Research/InterchangeFormat/"
* >specification</a>)
*/
public class XMLBIFWriter implements IBayesNetWriter {
private static final String XML_HEADER = "<?xml version=\"1.0\"?>\n";
private static final String COMMENT = "<!--\n\t Bayesian Network in XMLBIF v0.3 \n-->\n";
private static final String DTD = "<!-- DTD for the XMLBIF 0.3 format -->\n" + "<!DOCTYPE BIF [\n"
+ "\t<!ELEMENT BIF ( NETWORK )*>\n" + "\t\t<!ATTLIST BIF VERSION CDATA #REQUIRED>\n"
+ "\t<!ELEMENT NETWORK ( NAME, ( PROPERTY | VARIABLE | DEFINITION )* )>\n"
+ "\t<!ELEMENT NAME (#PCDATA)>\n" + "\t<!ELEMENT VARIABLE ( NAME, ( OUTCOME | PROPERTY )* ) >\n"
+ "\t\t<!ATTLIST VARIABLE TYPE (nature|decision|utility) \"nature\">\n"
+ "\t<!ELEMENT OUTCOME (#PCDATA)>\n" + "\t<!ELEMENT DEFINITION ( FOR | GIVEN | TABLE | PROPERTY )* >\n"
+ "\t<!ELEMENT FOR (#PCDATA)>\n" + "\t<!ELEMENT GIVEN (#PCDATA)>\n" + "\t<!ELEMENT TABLE (#PCDATA)>\n"
+ "\t<!ELEMENT PROPERTY (#PCDATA)>\n" + "]>\n";
private final Writer wrtr;
public XMLBIFWriter(OutputStream out) {
this.wrtr = new OutputStreamWriter(out);
}
public void write(BayesNet net) throws IOException {
StringBuilder bldr = new StringBuilder();
bldr.append(XML_HEADER);
bldr.append(COMMENT);
bldr.append(DTD);
int offset = bldr.length();
bldr.append(net.getName());
XMLUtil.surround(offset, bldr, NAME);
bldr.append("\n<!-- Variables -->\n");
writeVariables(bldr, net);
bldr.append("\n<!-- Probability Distributions -->\n");
writeVariableDefs(bldr, net);
XMLUtil.surround(offset, bldr, "NETWORK");
XMLUtil.surround(offset, bldr, "BIF", "VERSION", "0.3");
wrtr.write(bldr.toString());
wrtr.flush();
}
private void writeVariableDefs(StringBuilder bldr, BayesNet net) {
for (BayesNode node : net.getNodes()) {
int offset = bldr.length();
bldr.append(node.getName());
XMLUtil.surround(offset, bldr, FOR);
writeParents(bldr, node);
writeProbabilities(bldr, node);
XMLUtil.surround(offset, bldr, DEFINITION);
}
}
private void writeParents(StringBuilder bldr, BayesNode node) {
for (BayesNode parent : node.getParents()) {
bldr.append("\n\t");
int offset = bldr.length();
bldr.append(parent.getName());
XMLUtil.surround(offset, bldr, GIVEN);
}
}
private void writeProbabilities(StringBuilder bldr, BayesNode node) {
if (node.getProbabilities().length == 0) {
throw new IllegalArgumentException("Bayesian Network is broken: " + node.getName()
+ " has an empty conditional probability table");
}
int offset = bldr.length();
for (double d : node.getProbabilities()) {
bldr.append(d);
bldr.append(' ');
}
bldr.deleteCharAt(bldr.length() - 1); // delete last whitespace
XMLUtil.surround(offset, bldr, TABLE);
}
private void writeVariables(StringBuilder bldr, BayesNet net) {
for (BayesNode node : net.getNodes()) {
int offset = bldr.length();
bldr.append(node.getName());
XMLUtil.surround(offset, bldr, NAME);
bldr.append("\n");
for (String outcome : node.getOutcomes()) {
int offset2 = bldr.length();
bldr.append(StringEscapeUtils.escapeXml(outcome));
XMLUtil.surround(offset2, bldr, OUTCOME);
bldr.append("\n");
}
XMLUtil.surround(offset, bldr, VARIABLE);
}
}
@Override
public void close() throws IOException {
wrtr.close();
}
}