/******************************************************************************* * Copyright (c) 2013 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.jbif; import static org.eclipse.recommenders.jayes.io.jbif.Constants.*; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; import org.eclipse.recommenders.jayes.BayesNet; import org.eclipse.recommenders.jayes.BayesNode; import org.eclipse.recommenders.jayes.io.IBayesNetReader; import com.google.common.base.Charsets; import com.google.common.primitives.Doubles; import com.google.common.primitives.Ints; /** * Reader for the Jayes Binary Interchange Format (JBIF) written by {@link JayesBifWriter}. */ public class JayesBifReader implements IBayesNetReader { private InputStream in; public JayesBifReader(InputStream str) { in = str; } @Override public BayesNet read() throws IOException { return read(IOUtils.toByteArray(in)); } private BayesNet read(byte[] array) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(array); try { return readBayesNet(buffer); } catch (RuntimeException e) { throw new IOException("Malformed data", e); } } private BayesNet readBayesNet(ByteBuffer buffer) throws IOException { BayesNet bayesNet = new BayesNet(); readHeader(buffer); bayesNet.setName(readName(buffer)); int nrNodes = buffer.getInt(); for (int i = 0; i < nrNodes; i++) { readNodeDeclaration(bayesNet, buffer); } for (int i = 0; i < nrNodes; i++) { readNodeDefinition(bayesNet, bayesNet.getNode(i), buffer); } return bayesNet; } private void readHeader(ByteBuffer buffer) throws IOException { int magicNumber = buffer.getInt(); if (magicNumber != MAGIC_NUMBER) { throw new IOException("Wrong magic number: " + Integer.toHexString(magicNumber).toUpperCase()); } int formatVersion = buffer.getInt(); if (formatVersion != FORMAT_VERSION) { throw new IOException("Wrong JBIF format version: " + formatVersion); } } private String readName(ByteBuffer buffer) { int byteCount = buffer.getShort() & 0xFFFF; byte[] bytes = new byte[byteCount]; buffer.get(bytes); return new String(bytes, Charsets.UTF_8); } private void readNodeDeclaration(BayesNet bayesNet, ByteBuffer buffer) { BayesNode node = bayesNet.createNode(readName(buffer)); int outcomeCount = buffer.getInt(); String[] outcomes = new String[outcomeCount]; for (int i = 0; i < outcomeCount; i++) { outcomes[i] = readName(buffer); } node.addOutcomes(outcomes); } private void readNodeDefinition(BayesNet bayesNet, BayesNode node, ByteBuffer buffer) throws IOException { node.setParents(readParents(bayesNet, buffer)); node.setProbabilities(readCpt(buffer)); } private List<BayesNode> readParents(BayesNet bayesNet, ByteBuffer buffer) throws IOException { int parentCount = buffer.get() & 0xFF; int[] parentIds = new int[parentCount]; buffer.asIntBuffer().get(parentIds); buffer.position(buffer.position() + parentIds.length * Ints.BYTES); List<BayesNode> parents = new ArrayList<BayesNode>(parentCount); for (int parentId : parentIds) { parents.add(bayesNet.getNode(parentId)); } return parents; } private double[] readCpt(ByteBuffer buffer) throws IOException { int entryCount = buffer.getInt(); double[] probabilities = new double[entryCount]; buffer.asDoubleBuffer().get(probabilities); buffer.position(buffer.position() + probabilities.length * Doubles.BYTES); return probabilities; } @Override public void close() throws IOException { in.close(); } }