com.bigdata.rdf.axioms.BaseAxioms Maven / Gradle / Ivy
/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
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; version 2 of the License.
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Mar 30, 2005
*/
package com.bigdata.rdf.axioms;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.openrdf.model.Value;
import com.bigdata.btree.keys.IKeyBuilder;
import com.bigdata.btree.keys.KeyBuilder;
import com.bigdata.io.LongPacker;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.IVUtility;
import com.bigdata.rdf.model.BigdataStatement;
import com.bigdata.rdf.model.BigdataValueFactory;
import com.bigdata.rdf.model.BigdataValueFactoryImpl;
import com.bigdata.rdf.model.StatementEnum;
import com.bigdata.rdf.rio.StatementBuffer;
import com.bigdata.rdf.spo.ISPO;
import com.bigdata.rdf.spo.SPO;
import com.bigdata.rdf.spo.SPOKeyOrder;
import com.bigdata.rdf.store.AbstractTripleStore;
/**
* A collection of axioms.
*
* Axioms are generated by {@link AbstractTripleStore#create()} based on
* its configured properties. While the implementation class declares axioms in
* terms of RDF {@link Value}s, the {@link BaseAxioms} only retains the set of
* {s:p:o} tuples for the term identifiers corresponding those those
* {@link Value}s. That {s:p:o} tuple array is the serialized state of this
* class. When an {@link AbstractTripleStore} is reopened, the axioms are
* de-serialized from a property in the global row store.
*
* @author personickm
* @author Bryan Thompson
* @version $Id$
*/
public abstract class BaseAxioms implements Axioms, Externalizable {
// /**
// * The axioms in SPO order.
// */
// private transient BTree btree;
/**
* The axioms.
*/
private Set axioms;
// /**
// * Used to format keys for that {@link BTree}.
// */
// private transient SPOTupleSerializer tupleSer;
// /**
// * Non-null
iff the ctor specifies this value.
// */
// private final transient AbstractTripleStore db;
/**
* The namespace of the associated kb instance. This is used to materialize
* the appropriate {@link BigdataValueFactory}.
*/
private String namespace;
@Override
final public String getNamespace() {
return namespace;
}
/**
* The value factory to be used when creating axioms.
*
* @throws IllegalStateException
* unless the ctor variant was used that specifies the database.
*/
final protected BigdataValueFactory getValueFactory() {
return BigdataValueFactoryImpl.getInstance(namespace);
// return db.getValueFactory();
}
/**
* De-serialization constructor.
*/
protected BaseAxioms() {
// db = null;
}
/**
* Ctor variant used by {@link AbstractTripleStore#create()}.
*
* Note: When de-serializing a {@link BaseAxioms} object the zero-arg ctor
* will be used.
*
* @param namespace
* The namespace for the {@link AbstractTripleStore} instance.
*/
protected BaseAxioms(final String namespace) {
if(namespace == null)
throw new IllegalArgumentException();
// this.db = db;
this.namespace = namespace;
}
/**
* Uses {@link #addAxioms(Collection)} to collect the declared axioms and
* then writes the axioms onto the database specified to the
* {@link BaseAxioms#BaseAxioms(AbstractTripleStore)} ctor.
*
* @throws IllegalStateException
* if that ctor was not used.
*/
final public void init(final AbstractTripleStore db) {
// setup [axioms] collection.
final Set axioms = new LinkedHashSet(
200);
// obtain collection of axioms to be used.
addAxioms(axioms);
this.axioms = writeAxioms(db, axioms);
}
/**
* Adds all axioms declared by this class into axioms.
*
* Note: Subclasses MUST extend this method to add their axioms into the
* axioms collection.
*
* @param axioms
* A collection into which the axioms will be inserted.
*
* @throws IllegalArgumentException
* if the parameter is null
.
*/
protected void addAxioms(final Collection axioms) {
if (axioms == null)
throw new IllegalArgumentException();
// NOP.
}
/**
* Writes the axioms on the database, resolving {@link BigdataStatement}s to
* {@link SPO}s.
*
* @return The axioms expressed as {@link SPO}s.
*/
private Set writeAxioms(final AbstractTripleStore db,
final Collection axioms) {
if (db == null)
throw new IllegalArgumentException();
if (axioms == null)
throw new IllegalArgumentException();
final int naxioms = axioms.size();
final LinkedHashSet ret = new LinkedHashSet(naxioms);
if (naxioms > 0) {
// Note: min capacity of one handles case with no axioms.
final int capacity = Math.max(1, naxioms);
final MyStatementBuffer buffer = new MyStatementBuffer(db, capacity);
// final StatementBuffer buffer = new StatementBuffer(db, capacity);
//
// final IChangeLog changeLog = new IChangeLog() {
//
// @Override
// public void changeEvent(final IChangeRecord record) {
//
// final ISPO tmp = record.getStatement();
//
// final SPO spo = new SPO(tmp.s(), tmp.p(), tmp.o());
//
// ret.add( spo );
//
// }
//
// @Override
// public void transactionBegin() {
// }
//
// @Override
// public void transactionPrepare() {
// }
//
// @Override
// public void transactionCommited(long commitTime) {
// }
//
// @Override
// public void transactionAborted() {
// }
//
// @Override
// public void close() {
// }
// };
//
// buffer.setChangeLog(changeLog);
final Iterator itr = axioms.iterator();
while (itr.hasNext()) {
final BigdataStatement triple = itr.next();
assert triple.getStatementType() == StatementEnum.Axiom;
buffer.add(triple);
}
// write on the database.
buffer.flush();
// SPO[] exposed by our StatementBuffer subclass.
final SPO[] stmts = ((MyStatementBuffer)buffer).stmts;
for(SPO spo : stmts) {
ret.add(spo);
}
}
// The axioms as SPO objects.
return ret;
}
// /**
// * Create the {@link BTree} to hold the axioms.
// *
// * @param naxioms
// * The #of axioms (used to tune the branching factor).
// *
// * @throws IllegalStateException
// * if the {@link #btree} exists.
// */
// private void createBTree(final int naxioms) {
//
// if (btree != null)
// throw new IllegalStateException();
//
// // exact fill of the root leaf.
// final int branchingFactor = Math.max(Options.MIN_BRANCHING_FACTOR, naxioms );
//
// /*
// * Note: This uses a SimpleMemoryRawStore since we never explicitly
// * close the BaseAxioms class. Also, all data should be fully
// * buffered in the leaf of the btree so the btree will never touch
// * the store after it has been populated.
// */
// final IndexMetadata metadata = new IndexMetadata(UUID.randomUUID());
//
// metadata.setBranchingFactor(branchingFactor);
//
// tupleSer = new SPOTupleSerializer(SPOKeyOrder.SPO, false/* sids */);
//
// metadata.setTupleSerializer(tupleSer);
//
// btree = BTree.createTransient(metadata);
//// btree = BTree.create(new SimpleMemoryRawStore(), metadata);
//
// }
// /**
// * Builds an internal B+Tree that is used to quickly identify axioms based
// * on {s:p:o} term identifier tuples, and returns the distinct {s:p:o} term
// * identifier tuples for the declared axioms.
// *
// * Note: if the terms for the axioms are already in the lexicon and the
// * axioms are already in the database then this will not write on the
// * database, but it will still result in the SPO[] containing the axioms to
// * be defined in {@link MyStatementBuffer}.
// *
// * @param axioms
// */
// private void buildBTree(final Collection axioms) {
//
// /*
// * Fill the btree with the axioms in SPO order.
// *
// * Note: This should ALWAYS use the SPO key order even for quads since
// * we just want to test on the (s,p,o).
// *
// * @todo This would be MUCH faster with a hashmap on the SPOs.
// *
// * @todo There is no need to put the statement type into the in-memory
// * axioms. they are axioms after all. That is, we could just have the
// * keys and no values.
// */
//
// final int naxioms = axioms.size();
//
// createBTree(naxioms/* naxioms */);
//
// for (BigdataStatement spo : axioms) {
//
// btree.insert(tupleSer.serializeKey(spo), spo.getStatementType()
// .serialize());
//
// }
//
// }
// /**
// * The initial version. The s, p, and o of each axiom were written out as
// * long
integers.
// */
// private static final transient byte VERSION0 = 0;
/**
* The serialization format was changed when we introduced the TERMS index
* (as opposed to the TERM2ID and ID2TERM index). Up to that point, the s,
* p, and o components were always long
termIds assigned by the
* TERM2ID index. However, the refactor which introduced the TERMS index
* generalized the {@link IV}s further such that we can no longer rely on
* the long
termId encoding. Therefore, the serialization of
* the axioms was changed to the length of each {@link SPOKeyOrder#SPO}
* index key followed by the byte[]
comprising that key. This
* has the effect of using the {@link IV} representation directly within the
* serialization of the axioms.
*
* Note: In the initial version, the s, p, and o of each axiom were written
* out as long
integers. That version is no longer supported.
*
* This version also includes the namespace so we can obtain the
* appropriate {@link BigdataValueFactory} instance without requiring a
* reference to the {@link AbstractTripleStore}.
*/
private static final transient byte VERSION1 = 1;
/**
* The current version.
*/
private static final transient byte currentVersion = VERSION1;
@Override
public void readExternal(final ObjectInput in) throws IOException,
ClassNotFoundException {
final byte version = in.readByte();
switch (version) {
// case VERSION0:
// readVersion0(in);
// break;
case VERSION1:
readVersion1(in);
break;
default:
throw new UnsupportedOperationException("Unknown version: "
+ version);
}
}
// @SuppressWarnings("unchecked")
// private void readVersion0(final ObjectInput in) throws IOException {
//
// final long naxioms = LongPacker.unpackLong(in);
//
// if (naxioms < 0 || naxioms > Integer.MAX_VALUE)
// throw new IOException();
//
// createBTree((int) naxioms);
//
// for (int i = 0; i < naxioms; i++) {
//
// final IV s = new TermId(VTE.URI, in.readLong());
//
// final IV p = new TermId(VTE.URI, in.readLong());
//
// final IV o = new TermId(VTE.URI, in.readLong());
//
// final SPO spo = new SPO(s, p, o, StatementEnum.Axiom);
//
// btree.insert(tupleSer.serializeKey(spo), spo.getStatementType()
// .serialize());
//
// }
// }
private void readVersion1(final ObjectInput in) throws IOException {
namespace = in.readUTF();
final int naxioms = LongPacker.unpackInt(in);
// if (naxioms < 0 || naxioms > Integer.MAX_VALUE)
// throw new IOException();
axioms = new LinkedHashSet(naxioms);
// createBTree((int) naxioms);
for (int i = 0; i < naxioms; i++) {
final int nbytes = LongPacker.unpackInt(in);
final byte[] key = new byte[nbytes];
in.readFully(key);
final IV[] ivs = IVUtility.decodeAll(key);
if (ivs.length != 3)
throw new IOException("Expecting 3 IVs, not: "
+ Arrays.toString(ivs));
final IV s = ivs[0];
final IV p = ivs[1];
final IV o = ivs[2];
final SPO spo = new SPO(s, p, o, StatementEnum.Axiom);
// btree.insert(tupleSer.serializeKey(spo), spo.getStatementType()
// .serialize());
axioms.add(spo);
}
}
public void writeExternal(final ObjectOutput out) throws IOException {
// if (btree == null)
// throw new IllegalStateException();
out.writeByte(currentVersion);
switch(currentVersion) {
// case VERSION0: writeVersion0(out); break;
case VERSION1: writeVersion1(out); break;
default: throw new AssertionError();
}
}
// private void writeVersion0(final ObjectOutput out) throws IOException {
//
// final long naxioms = btree.rangeCount();
//
// LongPacker.packLong(out, naxioms);
//
// @SuppressWarnings("unchecked")
// final ITupleIterator itr = btree.rangeIterator();
//
// while (itr.hasNext()) {
//
// final SPO spo = itr.next().getObject();
//
// out.writeLong(spo.s().getTermId());
//
// out.writeLong(spo.p().getTermId());
//
// out.writeLong(spo.o().getTermId());
//
// }
//
// }
private void writeVersion1(final ObjectOutput out) throws IOException {
out.writeUTF(namespace);
LongPacker.packLong(out, axioms.size());
final IKeyBuilder keyBuilder = new KeyBuilder(24/*initialCapacity*/);
for (ISPO spo : axioms) {
final byte[] key = SPOKeyOrder.SPO.encodeKey(keyBuilder, spo);
if (true) {
final IV[] ivs = IVUtility.decodeAll(key);
if (ivs.length != 3)
throw new IOException("Expecting 3 IVs, not: "
+ Arrays.toString(ivs) + " for " + spo);
final IV s = ivs[0];
final IV p = ivs[1];
final IV o = ivs[2];
final SPO spo2 = new SPO(s, p, o, StatementEnum.Axiom);
if (!spo.equals(spo2))
throw new IOException("Expecting: " + spo + ", not " + spo2);
}
LongPacker.packLong(out, key.length);
out.write(key);
}
}
final public boolean isAxiom(final IV s, final IV p, final IV o) {
if (axioms == null)
throw new IllegalStateException();
// fast rejection.
if (s == null || p == null || o == null) {
return false;
}
final SPO spo = new SPO(s, p, o, StatementEnum.Axiom);
if(axioms.contains(spo)) {
return true;
}
return false;
}
@Override
public final int size() {
if (axioms == null)
throw new IllegalStateException();
return axioms.size();
}
@Override
final public Iterator axioms() {
if (axioms == null)
throw new IllegalStateException();
return Collections.unmodifiableSet(axioms).iterator();
}
/**
* Helper class.
*
* @author Bryan Thompson
*/
private static class MyStatementBuffer extends StatementBuffer
implements StatementBuffer.IWrittenSPOArray {
/**
* An array of the axioms in SPO order.
*/
private SPO[] stmts;
/**
* @param database
* @param capacity
*/
public MyStatementBuffer(final AbstractTripleStore database,
final int capacity) {
super(database, capacity);
didWriteCallback = this;
}
/**
* Overridden to save off a copy of the axioms in SPO order on
* {@link #stmts} where we can access them afterwards.
*/
@Override
public void didWriteSPOs(final SPO[] stmts, final int numStmts) {
if (this.stmts == null) {
this.stmts = new SPO[numStmts];
System.arraycopy(stmts, 0, this.stmts, 0, numStmts);
Arrays.sort( this.stmts, SPOKeyOrder.SPO.getComparator() );
}
// super.didWriteSPOs(stmts, numStmts);
}
}
}