001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 2007.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.sail.memory;
007:
008: import java.io.DataInputStream;
009: import java.io.DataOutputStream;
010: import java.io.File;
011: import java.io.FileInputStream;
012: import java.io.FileOutputStream;
013: import java.io.IOException;
014: import java.io.InputStream;
015: import java.io.OutputStream;
016: import java.util.Arrays;
017: import java.util.zip.GZIPInputStream;
018: import java.util.zip.GZIPOutputStream;
019:
020: import info.aduna.io.IOUtil;
021: import info.aduna.iteration.CloseableIteration;
022:
023: import org.openrdf.model.BNode;
024: import org.openrdf.model.Literal;
025: import org.openrdf.model.Namespace;
026: import org.openrdf.model.Resource;
027: import org.openrdf.model.URI;
028: import org.openrdf.model.Value;
029: import org.openrdf.sail.SailException;
030: import org.openrdf.sail.memory.model.MemResource;
031: import org.openrdf.sail.memory.model.MemStatement;
032: import org.openrdf.sail.memory.model.MemURI;
033: import org.openrdf.sail.memory.model.MemValue;
034: import org.openrdf.sail.memory.model.ReadMode;
035:
036: /**
037: * Functionality to read and write MemoryStore to/from a file.
038: *
039: * @author Arjohn Kampman
040: */
041: class FileIO {
042:
043: /** Magic number for Binary Memory Store Files */
044: private static final byte[] MAGIC_NUMBER = new byte[] { 'B', 'M',
045: 'S', 'F' };
046:
047: /** The version number of the current format. */
048: private static final int BMSF_VERSION = 1;
049:
050: /* RECORD TYPES */
051: public static final int NAMESPACE_MARKER = 1;
052:
053: public static final int EXPL_TRIPLE_MARKER = 2;
054:
055: public static final int EXPL_QUAD_MARKER = 3;
056:
057: public static final int INF_TRIPLE_MARKER = 4;
058:
059: public static final int INF_QUAD_MARKER = 5;
060:
061: public static final int URI_MARKER = 6;
062:
063: public static final int BNODE_MARKER = 7;
064:
065: public static final int PLAIN_LITERAL_MARKER = 8;
066:
067: public static final int LANG_LITERAL_MARKER = 9;
068:
069: public static final int DATATYPE_LITERAL_MARKER = 10;
070:
071: public static final int EOF_MARKER = 127;
072:
073: public static void write(MemoryStore store, File dataFile)
074: throws IOException, SailException {
075: OutputStream out = new FileOutputStream(dataFile);
076: try {
077: // Write header
078: out.write(MAGIC_NUMBER);
079: out.write(BMSF_VERSION);
080:
081: // The rest of the data is GZIP-compressed
082: DataOutputStream dataOut = new DataOutputStream(
083: new GZIPOutputStream(out));
084: out = dataOut;
085:
086: writeNamespaces(store, dataOut);
087:
088: writeStatements(store, dataOut);
089:
090: dataOut.writeByte(EOF_MARKER);
091: } finally {
092: out.close();
093: }
094: }
095:
096: public static void read(MemoryStore store, File dataFile)
097: throws IOException {
098: InputStream in = new FileInputStream(dataFile);
099: try {
100: byte[] magicNumber = IOUtil.readBytes(in,
101: MAGIC_NUMBER.length);
102: if (!Arrays.equals(magicNumber, MAGIC_NUMBER)) {
103: throw new IOException(
104: "File is not a binary MemoryStore file");
105: }
106:
107: int version = in.read();
108: if (version != BMSF_VERSION) {
109: throw new IOException("Incompatible format version: "
110: + version);
111: }
112:
113: // The rest of the data is GZIP-compressed
114: DataInputStream dataIn = new DataInputStream(
115: new GZIPInputStream(in));
116: in = dataIn;
117:
118: int recordTypeMarker;
119: while ((recordTypeMarker = dataIn.readByte()) != EOF_MARKER) {
120: switch (recordTypeMarker) {
121: case NAMESPACE_MARKER:
122: readNamespace(store, dataIn);
123: break;
124: case EXPL_TRIPLE_MARKER:
125: readStatement(store, false, true, dataIn);
126: break;
127: case EXPL_QUAD_MARKER:
128: readStatement(store, true, true, dataIn);
129: break;
130: case INF_TRIPLE_MARKER:
131: readStatement(store, false, false, dataIn);
132: break;
133: case INF_QUAD_MARKER:
134: readStatement(store, true, false, dataIn);
135: break;
136: default:
137: throw new IOException(
138: "Invalid record type marker: "
139: + recordTypeMarker);
140: }
141: }
142: } finally {
143: in.close();
144: }
145: }
146:
147: private static void writeNamespaces(MemoryStore store,
148: DataOutputStream dataOut) throws IOException {
149: for (Namespace ns : store.getNamespaceStore()) {
150: dataOut.writeByte(NAMESPACE_MARKER);
151: dataOut.writeUTF(ns.getPrefix());
152: dataOut.writeUTF(ns.getName());
153:
154: // FIXME dummy boolean to be compatible with older version:
155: // the up-to-date status is no longer relevant
156: dataOut.writeBoolean(true);
157: }
158: }
159:
160: private static void readNamespace(MemoryStore store,
161: DataInputStream dataIn) throws IOException {
162: String prefix = dataIn.readUTF();
163: String name = dataIn.readUTF();
164:
165: // FIXME dummy boolean to be compatible with older version:
166: // the up-to-date status is no longer relevant
167: dataIn.readBoolean();
168:
169: store.getNamespaceStore().setNamespace(prefix, name);
170: }
171:
172: private static void writeStatements(MemoryStore store,
173: DataOutputStream dataOut) throws IOException, SailException {
174: CloseableIteration<MemStatement, SailException> stIter = store
175: .createStatementIterator(SailException.class, null,
176: null, null, false, store.getCurrentSnapshot(),
177: ReadMode.COMMITTED);
178:
179: try {
180: while (stIter.hasNext()) {
181: MemStatement st = stIter.next();
182: Resource context = st.getContext();
183:
184: if (st.isExplicit()) {
185: if (context == null) {
186: dataOut.writeByte(EXPL_TRIPLE_MARKER);
187: } else {
188: dataOut.writeByte(EXPL_QUAD_MARKER);
189: }
190: } else {
191: if (context == null) {
192: dataOut.writeByte(INF_TRIPLE_MARKER);
193: } else {
194: dataOut.writeByte(INF_QUAD_MARKER);
195: }
196: }
197:
198: writeValue(st.getSubject(), dataOut);
199: writeValue(st.getPredicate(), dataOut);
200: writeValue(st.getObject(), dataOut);
201: if (context != null) {
202: writeValue(context, dataOut);
203: }
204: }
205: } finally {
206: stIter.close();
207: }
208: }
209:
210: private static void readStatement(MemoryStore store,
211: boolean hasContext, boolean isExplicit,
212: DataInputStream dataIn) throws IOException,
213: ClassCastException {
214: MemResource memSubj = (MemResource) readValue(store, dataIn);
215: MemURI memPred = (MemURI) readValue(store, dataIn);
216: MemValue memObj = (MemValue) readValue(store, dataIn);
217: MemResource memContext = null;
218: if (hasContext) {
219: memContext = (MemResource) readValue(store, dataIn);
220: }
221:
222: MemStatement st = new MemStatement(memSubj, memPred, memObj,
223: memContext, isExplicit, store.getCurrentSnapshot());
224: store.getStatements().add(st);
225: st.addToComponentLists();
226: }
227:
228: private static void writeValue(Value value, DataOutputStream dataOut)
229: throws IOException {
230: if (value instanceof URI) {
231: dataOut.writeByte(URI_MARKER);
232: dataOut.writeUTF(((URI) value).toString());
233: } else if (value instanceof BNode) {
234: dataOut.writeByte(BNODE_MARKER);
235: dataOut.writeUTF(((BNode) value).getID());
236: } else if (value instanceof Literal) {
237: Literal lit = (Literal) value;
238:
239: String label = lit.getLabel();
240: String language = lit.getLanguage();
241: URI datatype = lit.getDatatype();
242:
243: if (datatype != null) {
244: dataOut.writeByte(DATATYPE_LITERAL_MARKER);
245: dataOut.writeUTF(label);
246: writeValue(datatype, dataOut);
247: } else if (language != null) {
248: dataOut.writeByte(LANG_LITERAL_MARKER);
249: dataOut.writeUTF(label);
250: dataOut.writeUTF(language);
251: } else {
252: dataOut.writeByte(PLAIN_LITERAL_MARKER);
253: dataOut.writeUTF(label);
254: }
255: } else {
256: throw new IllegalArgumentException(
257: "unexpected value type: " + value.getClass());
258: }
259: }
260:
261: private static Value readValue(MemoryStore store,
262: DataInputStream dataIn) throws IOException,
263: ClassCastException {
264: int valueTypeMarker = dataIn.readByte();
265:
266: if (valueTypeMarker == URI_MARKER) {
267: String uriString = dataIn.readUTF();
268: return store.getValueFactory().createURI(uriString);
269: } else if (valueTypeMarker == BNODE_MARKER) {
270: String bnodeID = dataIn.readUTF();
271: return store.getValueFactory().createBNode(bnodeID);
272: } else if (valueTypeMarker == PLAIN_LITERAL_MARKER) {
273: String label = dataIn.readUTF();
274: return store.getValueFactory().createLiteral(label);
275: } else if (valueTypeMarker == LANG_LITERAL_MARKER) {
276: String label = dataIn.readUTF();
277: String language = dataIn.readUTF();
278: return store.getValueFactory().createLiteral(label,
279: language);
280: } else if (valueTypeMarker == DATATYPE_LITERAL_MARKER) {
281: String label = dataIn.readUTF();
282: URI datatype = (URI) readValue(store, dataIn);
283: return store.getValueFactory().createLiteral(label,
284: datatype);
285: } else {
286: throw new IOException("Invalid value type marker: "
287: + valueTypeMarker);
288: }
289: }
290: }
|