/* * Copyright Aduna (http://www.aduna-software.com/) (c) 2007. * * Licensed under the Aduna BSD-style license. */ package org.openrdf.sail.memory; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import info.aduna.io.IOUtil; import info.aduna.iteration.CloseableIteration; import org.openrdf.model.BNode; import org.openrdf.model.Literal; import org.openrdf.model.Namespace; import org.openrdf.model.Resource; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.sail.SailException; import org.openrdf.sail.memory.model.MemResource; import org.openrdf.sail.memory.model.MemStatement; import org.openrdf.sail.memory.model.MemURI; import org.openrdf.sail.memory.model.MemValue; import org.openrdf.sail.memory.model.ReadMode; /** * Functionality to read and write MemoryStore to/from a file. * * @author Arjohn Kampman */ class FileIO { /** Magic number for Binary Memory Store Files */ private static final byte[] MAGIC_NUMBER = new byte[] { 'B', 'M', 'S', 'F' }; /** The version number of the current format. */ private static final int BMSF_VERSION = 1; /* RECORD TYPES */ public static final int NAMESPACE_MARKER = 1; public static final int EXPL_TRIPLE_MARKER = 2; public static final int EXPL_QUAD_MARKER = 3; public static final int INF_TRIPLE_MARKER = 4; public static final int INF_QUAD_MARKER = 5; public static final int URI_MARKER = 6; public static final int BNODE_MARKER = 7; public static final int PLAIN_LITERAL_MARKER = 8; public static final int LANG_LITERAL_MARKER = 9; public static final int DATATYPE_LITERAL_MARKER = 10; public static final int EOF_MARKER = 127; public static void write(MemoryStore store, File dataFile) throws IOException, SailException { OutputStream out = new FileOutputStream(dataFile); try { // Write header out.write(MAGIC_NUMBER); out.write(BMSF_VERSION); // The rest of the data is GZIP-compressed DataOutputStream dataOut = new DataOutputStream(new GZIPOutputStream(out)); out = dataOut; writeNamespaces(store, dataOut); writeStatements(store, dataOut); dataOut.writeByte(EOF_MARKER); } finally { out.close(); } } public static void read(MemoryStore store, File dataFile) throws IOException { InputStream in = new FileInputStream(dataFile); try { byte[] magicNumber = IOUtil.readBytes(in, MAGIC_NUMBER.length); if (!Arrays.equals(magicNumber, MAGIC_NUMBER)) { throw new IOException("File is not a binary MemoryStore file"); } int version = in.read(); if (version != BMSF_VERSION) { throw new IOException("Incompatible format version: " + version); } // The rest of the data is GZIP-compressed DataInputStream dataIn = new DataInputStream(new GZIPInputStream(in)); in = dataIn; int recordTypeMarker; while ((recordTypeMarker = dataIn.readByte()) != EOF_MARKER) { switch (recordTypeMarker) { case NAMESPACE_MARKER: readNamespace(store, dataIn); break; case EXPL_TRIPLE_MARKER: readStatement(store, false, true, dataIn); break; case EXPL_QUAD_MARKER: readStatement(store, true, true, dataIn); break; case INF_TRIPLE_MARKER: readStatement(store, false, false, dataIn); break; case INF_QUAD_MARKER: readStatement(store, true, false, dataIn); break; default: throw new IOException("Invalid record type marker: " + recordTypeMarker); } } } finally { in.close(); } } private static void writeNamespaces(MemoryStore store, DataOutputStream dataOut) throws IOException { for (Namespace ns : store.getNamespaceStore()) { dataOut.writeByte(NAMESPACE_MARKER); dataOut.writeUTF(ns.getPrefix()); dataOut.writeUTF(ns.getName()); // FIXME dummy boolean to be compatible with older version: // the up-to-date status is no longer relevant dataOut.writeBoolean(true); } } private static void readNamespace(MemoryStore store, DataInputStream dataIn) throws IOException { String prefix = dataIn.readUTF(); String name = dataIn.readUTF(); // FIXME dummy boolean to be compatible with older version: // the up-to-date status is no longer relevant dataIn.readBoolean(); store.getNamespaceStore().setNamespace(prefix, name); } private static void writeStatements(MemoryStore store, DataOutputStream dataOut) throws IOException, SailException { CloseableIteration<MemStatement, SailException> stIter = store.createStatementIterator( SailException.class, null, null, null, false, store.getCurrentSnapshot(), ReadMode.COMMITTED); try { while (stIter.hasNext()) { MemStatement st = stIter.next(); Resource context = st.getContext(); if (st.isExplicit()) { if (context == null) { dataOut.writeByte(EXPL_TRIPLE_MARKER); } else { dataOut.writeByte(EXPL_QUAD_MARKER); } } else { if (context == null) { dataOut.writeByte(INF_TRIPLE_MARKER); } else { dataOut.writeByte(INF_QUAD_MARKER); } } writeValue(st.getSubject(), dataOut); writeValue(st.getPredicate(), dataOut); writeValue(st.getObject(), dataOut); if (context != null) { writeValue(context, dataOut); } } } finally { stIter.close(); } } private static void readStatement(MemoryStore store, boolean hasContext, boolean isExplicit, DataInputStream dataIn) throws IOException, ClassCastException { MemResource memSubj = (MemResource)readValue(store, dataIn); MemURI memPred = (MemURI)readValue(store, dataIn); MemValue memObj = (MemValue)readValue(store, dataIn); MemResource memContext = null; if (hasContext) { memContext = (MemResource)readValue(store, dataIn); } MemStatement st = new MemStatement(memSubj, memPred, memObj, memContext, isExplicit, store.getCurrentSnapshot()); store.getStatements().add(st); st.addToComponentLists(); } private static void writeValue(Value value, DataOutputStream dataOut) throws IOException { if (value instanceof URI) { dataOut.writeByte(URI_MARKER); dataOut.writeUTF(((URI)value).toString()); } else if (value instanceof BNode) { dataOut.writeByte(BNODE_MARKER); dataOut.writeUTF(((BNode)value).getID()); } else if (value instanceof Literal) { Literal lit = (Literal)value; String label = lit.getLabel(); String language = lit.getLanguage(); URI datatype = lit.getDatatype(); if (datatype != null) { dataOut.writeByte(DATATYPE_LITERAL_MARKER); dataOut.writeUTF(label); writeValue(datatype, dataOut); } else if (language != null) { dataOut.writeByte(LANG_LITERAL_MARKER); dataOut.writeUTF(label); dataOut.writeUTF(language); } else { dataOut.writeByte(PLAIN_LITERAL_MARKER); dataOut.writeUTF(label); } } else { throw new IllegalArgumentException("unexpected value type: " + value.getClass()); } } private static Value readValue(MemoryStore store, DataInputStream dataIn) throws IOException, ClassCastException { int valueTypeMarker = dataIn.readByte(); if (valueTypeMarker == URI_MARKER) { String uriString = dataIn.readUTF(); return store.getValueFactory().createURI(uriString); } else if (valueTypeMarker == BNODE_MARKER) { String bnodeID = dataIn.readUTF(); return store.getValueFactory().createBNode(bnodeID); } else if (valueTypeMarker == PLAIN_LITERAL_MARKER) { String label = dataIn.readUTF(); return store.getValueFactory().createLiteral(label); } else if (valueTypeMarker == LANG_LITERAL_MARKER) { String label = dataIn.readUTF(); String language = dataIn.readUTF(); return store.getValueFactory().createLiteral(label, language); } else if (valueTypeMarker == DATATYPE_LITERAL_MARKER) { String label = dataIn.readUTF(); URI datatype = (URI)readValue(store, dataIn); return store.getValueFactory().createLiteral(label, datatype); } else { throw new IOException("Invalid value type marker: " + valueTypeMarker); } } }