com.bigdata.rdf.spo.SPO 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
*/
package com.bigdata.rdf.spo;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import com.bigdata.bop.IConstant;
import com.bigdata.bop.IPredicate;
import com.bigdata.bop.IVariableOrConstant;
import com.bigdata.rdf.inf.Justification;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.IVCache;
import com.bigdata.rdf.internal.IVUtility;
import com.bigdata.rdf.internal.NotMaterializedException;
import com.bigdata.rdf.internal.impl.bnode.SidIV;
import com.bigdata.rdf.model.BigdataResource;
import com.bigdata.rdf.model.BigdataStatement;
import com.bigdata.rdf.model.BigdataStatementImpl;
import com.bigdata.rdf.model.BigdataURI;
import com.bigdata.rdf.model.BigdataValue;
import com.bigdata.rdf.model.StatementEnum;
import com.bigdata.rdf.store.AbstractTripleStore;
import com.bigdata.rdf.store.IRawTripleStore;
import com.bigdata.relation.accesspath.IAccessPath;
import com.bigdata.util.Bits;
/**
* Represents a triple, triple+SID, or quad. When used to represent a triple,
* the statement identifier MAY be set on the triple after the object has been
* instantiated. When used to represent a quad, the context position SHOULD be
* treated as immutable and {@link #setStatementIdentifier(IV)} will reject
* arguments if they can not be validated as statement identifiers (based on
* their bit pattern).
*
* @author Bryan Thompson
* @version $Id$
*/
public class SPO implements ISPO, java.io.Serializable {
/**
* Note: Serializable for interchange of {@link SidIV}s.
*/
private static final long serialVersionUID = 1L;
/** The internal value for the subject position. */
@SuppressWarnings("rawtypes")
public final IV s;
/** The internal value for the predicate position. */
@SuppressWarnings("rawtypes")
public final IV p;
/** The internal value for the object position. */
@SuppressWarnings("rawtypes")
public final IV o;
/** The internal value for the context position. */
@SuppressWarnings("rawtypes")
private final IV c;
/**
* The statement identifier (optional).
*
* Note: this is not final since we create it only on demand.
*/
@SuppressWarnings("rawtypes")
private IV sid = null;
// /**
// * Statement type (inferred, explicit, or axiom).
// */
// private StatementEnum type;
//
// /**
// * User flag
// */
// private boolean userFlag;
//
// /**
// * Override flag used for downgrading statements during truth maintenance.
// */
// private transient boolean override = false;
//
//// private transient boolean modified = false;
// private transient ModifiedEnum modified = ModifiedEnum.NONE;
//
// /**
// * If sidable, we will lazily instantiate a sid when requested via
// * {@link #c()}, {@link #getStatementIdentifier()}, and {@link SPO#get(int)}
// * with a parameter of 3. This should reduce heap pressure by only creating
// * sids on-demand on an as-needed basis.
// */
// private boolean sidable = false;
/**
* Bit flags used to represent statement type, user flag, override,
* modified enum, and sidable flag. Much more compact representation.
*/
public byte flags = 0;
/**
* Denotes which bit to find the StatementType within the {@link #flags}.
* Type takes two bits.
*/
private static int TYPE_BIT = 0;
/**
* Denotes which bit to find the ModifiedEnum within the {@link #flags}.
* Modified takes two bits.
*/
private static int MODIFIED_BIT = 3;
/**
* Denotes which bit to find the userFlag within the {@link #flags}.
*/
private static int USERFLAG_BIT = 5;
/**
* Denotes which bit to find the override flag within the {@link #flags}.
*/
private static int OVERRIDE_BIT = 6;
// /**
// * Denotes which bit to find the sidable flag within the {@link #flags}.
// */
// private static int SIDABLE_BIT = 7;
@Override
@SuppressWarnings("rawtypes")
final public IV get(final int index) {
switch(index) {
case 0: return s;
case 1: return p;
case 2: return o;
case 3: return c;
default: throw new IllegalArgumentException();
}
}
@Override
@SuppressWarnings("rawtypes")
final public IV s() {
return s;
}
@Override
@SuppressWarnings("rawtypes")
final public IV p() {
return p;
}
@Override
@SuppressWarnings("rawtypes")
final public IV o() {
return o;
}
@Override
@SuppressWarnings("rawtypes")
final public IV c() {
return c;
}
// @Override
// public final void setStatementIdentifier(final boolean sid) {
//
// if (sid && type() != StatementEnum.Explicit) {
//
// // Only allowed for explicit statements.
// throw new IllegalStateException();
//
// }
//
// sidable(sid);
//
// // clear the current value for c
// this.sid = null;
//
// }
@SuppressWarnings("rawtypes")
public final IV getStatementIdentifier() {
// lazy instantiate the sid if necessary
if (sid == null && type() == StatementEnum.Explicit) {
sid = new SidIV(this);
}
return sid;
}
@Override
final public boolean hasStatementIdentifier() {
return type() == StatementEnum.Explicit;
}
@Override
public void setOverride(final boolean override) {
override(override);
}
@Override
public boolean isOverride() {
return override();
}
/**
* Triple constructor for a statement whose type is NOT known.
*
* Note: This is primarily used when you want to discover the
* type of the statement.
*
* @see AbstractTripleStore#bulkCompleteStatements(ISPOIterator)
*/
public SPO(final IV s, final IV p, final IV o) {
this.s = s;
this.p = p;
this.o = o;
this.c = null;
type(null);
}
/**
* Quads constructor.
*
* @param s
* @param p
* @param o
* @param c
*/
public SPO(final IV s, final IV p, final IV o, final IV c) {
this.s = s;
this.p = p;
this.o = o;
this.c = c;
type(null);
}
/**
* Construct a triple.
*
* Note: When the statement is {@link StatementEnum#Inferred} you MUST also
* construct the appropriate {@link Justification}.
*
* @param s
* @param p
* @param o
* @param type
* The statement type.
*/
public SPO(final IV s, final IV p, final IV o, StatementEnum type) {
if (type == null)
throw new IllegalArgumentException();
this.s = s;
this.p = p;
this.o = o;
this.c = null;
type(type);
}
/**
* Quads constructor with {@link StatementEnum}.
* @param s
* @param p
* @param o
* @param c
* @param type
*/
public SPO(final IV s, final IV p, final IV o, final IV c,
final StatementEnum type) {
if (type == null)
throw new IllegalArgumentException();
this.s = s;
this.p = p;
this.o = o;
this.c = c;
type(type);
}
/**
* Variant to create an {@link SPO} from constants (used by the unit tests).
*
* @param s
* @param p
* @param o
* @param type
*/
public SPO(final IConstant s, final IConstant p,
final IConstant o, final StatementEnum type) {
this(s.get(), p.get(), o.get(), type);
}
/**
* Variant to create an SPO from a predicate - the {@link StatementEnum} and
* statement identifier are not specified. This may be used as a convenience
* to extract the {s, p, o, c} from an {@link IPredicate} or from an
* {@link IAccessPath} when the predicate is not known to be an
* {@link SPOPredicate}.
*
* @param predicate
* The predicate.
*/
@SuppressWarnings("unchecked")
public SPO(final IPredicate predicate) {
{
final IVariableOrConstant t = predicate.get(0);
s = t.isVar() ? null : t.get();
}
{
final IVariableOrConstant t = predicate.get(1);
p = t.isVar() ? null : t.get();
}
{
final IVariableOrConstant t = predicate.get(2);
o = t.isVar() ? null : t.get();
}
if (predicate.arity() >= 4) {
final IVariableOrConstant t = predicate.get(3);
c = t.isVar() ? null : t.get();
} else {
c = null;
}
}
/**
* Construct a triple from {@link BigdataValue}s and the specified statement
* type.
*
* @param s
* @param p
* @param o
* @param type
*/
public SPO(final BigdataResource s, final BigdataURI p,
final BigdataValue o, final StatementEnum type) {
this(s.getIV(), p.getIV(), o.getIV(), type);
}
/**
* Construct a triple/quad from a {@link BigdataStatement}. The term
* identifiers and statement type information available on the
* {@link BigdataStatement} will be used to initialize the {@link SPO}.
*
* @param stmt
* The statement.
*/
public SPO(final BigdataStatement stmt) {
this( stmt.s(),//
stmt.p(),//
stmt.o(),//
stmt.c(),//
stmt.getStatementType()//
);
}
/**
* Return true
IFF the {@link SPO} is marked as {@link StatementEnum#Explicit}.
*/
@Override
public final boolean isExplicit() {
return type() == StatementEnum.Explicit;
}
/**
* Return true
IFF the {@link SPO} is marked as {@link StatementEnum#Inferred}.
*/
@Override
public final boolean isInferred() {
return type() == StatementEnum.Inferred;
}
/**
* Return true
IFF the {@link SPO} is marked as {@link StatementEnum#Axiom}.
*/
@Override
public final boolean isAxiom() {
return type() == StatementEnum.Axiom;
}
/**
* Return true
IFF the {@link SPO} has the user flag bit set.
*/
@Override
public final boolean getUserFlag() {
return userFlag();
}
/**
* Set the user flag bit on this SPO.
*
* @parm userFlag boolean flag
*/
@Override
public final void setUserFlag(final boolean userFlag) {
userFlag(userFlag);
}
private int hashCode = 0;
/**
* Hash code for the (s,p,o) per Sesame's {@link Statement#hashCode()}. It
* DOES NOT consider the context position.
*/
@Override
public int hashCode() {
if (hashCode == 0) {
final int s = this.s.hashCode();
final int p = this.p.hashCode();
final int o = this.o.hashCode();
/*
* Note: The historical behavior was based on the int64 term
* identifiers. Since the hash code is now computed from the int32
* hash codes of the (s,p,o) IV objects, the original bit math was
* resulting in a hash code which was always zero (any 32 bit value
* shifted right by 32 bits is zero).
*/
hashCode = 961 * s + 31 * p + o;
// hashCode = 961 * ((int) (s ^ (s >>> 32))) + 31
// * ((int) (p ^ (p >>> 32))) + ((int) (o ^ (o >>> 32)));
}
return hashCode;
}
@Override
public boolean equals(final Object o) {
if (this == o)
return true;
if (o == null)
return false;
return equals((ISPO) o);
}
/**
* True iff the {@link ISPO}s are the same object or if the same term
* identifiers are assigned for the subject, predicate and object positions,
* and the same {@link StatementEnum} are the same.
*
* Note: This is NOT the same test as
* {@link BigdataStatementImpl#equals(Object)} since the latter is
* implemented per the {@link Statement} interface.
*/
public boolean equals(final ISPO stmt2) {
if (stmt2 == this)
return true;
return
IVUtility.equals(this.s, stmt2.s()) && //
IVUtility.equals(this.p, stmt2.p()) && //
IVUtility.equals(this.o, stmt2.o()) && //
type() == stmt2.getStatementType()
;
}
/**
* Return a representation of the statement using the term identifiers (the
* identifiers are NOT resolved to terms).
*
* @see ITripleStore#toString(IV, IV, IV)
*/
@Override
public String toString() {
return ("< " + toString(s) + ", " + toString(p) + ", " + toString(o))
+ (c == null ? "" : ", " + toString(c))
+ (type() == null ? "" : " : " + type()
+ (override() ? ", override" : ""))
+ (isModified() ? ", modified ("+modified()+")" : "") + " >";
}
/**
* Represents the term identifier together with its type (literal, bnode,
* uri, or statement identifier).
*
* @param iv
* The term identifier.
* @return
*/
@SuppressWarnings("rawtypes")
public static String toString(final IV iv) {
if (iv == null)
return IRawTripleStore.NULLSTR;
return iv.toString();
}
/**
* Resolves the term identifiers to terms against the store and returns a
* representation of the statement using
* {@link IRawTripleStore#toString(IV, IV, IV)}.
*
* @param store
* The store (optional). When non-null
the store
* will be used to resolve term identifiers to terms.
*
* @return The externalized representation of the statement.
*/
@Override
public String toString(final IRawTripleStore store) {
if (store != null) {
String t = null;
if (type() != null) {
switch(type()) {
case Explicit : t = "Explicit "; break;
case Inferred : t = "Inferred "; break;
case Axiom : t = "Axiom "; break;
case History : t = "History "; break;
default: throw new AssertionError();
}
} else {
t = "Unknown ";
}
return t + (isModified() ? "(*)" : "") + " : "
+ store.toString(s, p, o, c);
} else {
return toString();
}
}
@Override
final public boolean isFullyBound() {
return s != null && p != null && o != null;
}
@Override
final public StatementEnum getStatementType() {
return type();
}
@Override
final public void setStatementType(final StatementEnum type) {
if(this.type() != null && this.type() != type) {
throw new IllegalStateException("newValue="+type+", spo="+this);
}
type(type);
}
@Override
final public boolean hasStatementType() {
return type() != null;
}
@Override
public boolean isModified() {
return modified() != ModifiedEnum.NONE;
}
@Override
public void setModified(final ModifiedEnum modified) {
modified(modified);
}
@Override
public ModifiedEnum getModified() {
return modified();
}
/**
* Statement type is hiding in the 0 and 1 bits of the flags.
*/
private StatementEnum type() {
// get just the 0 and 1 and 2 bits
// final byte b = Bits.mask(flags, 0, 1, 2);
byte b = 0;
b |= (0x1 << TYPE_BIT);
b |= (0x1 << (TYPE_BIT+1));
b |= (0x1 << (TYPE_BIT+2));
b &= flags;
switch (b) {
case 0: return null;
case 1: return StatementEnum.Explicit;
case 2: return StatementEnum.Axiom;
case 3: return StatementEnum.Inferred;
case 4: return StatementEnum.History;
}
throw new IllegalStateException();
}
/**
* Statement type is hiding in the 0 and 1 and 2 bits of the flags.
*/
private void type(final StatementEnum type) {
byte b = flags;
if (type == null) {
b = Bits.set(Bits.set(Bits.set(b, TYPE_BIT, false), (TYPE_BIT+1), false), (TYPE_BIT+2), false);
} else {
switch(type) {
case Explicit:
b = Bits.set(Bits.set(Bits.set(b, TYPE_BIT, true), (TYPE_BIT+1), false), (TYPE_BIT+2), false);
break;
case Axiom:
b = Bits.set(Bits.set(Bits.set(b, TYPE_BIT, false), (TYPE_BIT+1), true), (TYPE_BIT+2), false);
break;
case Inferred:
b = Bits.set(Bits.set(Bits.set(b, TYPE_BIT, true), (TYPE_BIT+1), true), (TYPE_BIT+2), false);
break;
case History:
b = Bits.set(Bits.set(Bits.set(b, TYPE_BIT, false), (TYPE_BIT+1), false), (TYPE_BIT+2), true);
break;
default:
throw new IllegalStateException();
}
}
flags = b;
}
/**
* Modified enum is hiding in the 3 and 4 bits of the flags.
*/
private ModifiedEnum modified() {
// get just the 3 and 4 bits
// final byte b = Bits.mask(flags, 3, 4);
byte b = 0;
b |= (0x1 << MODIFIED_BIT);
b |= (0x1 << (MODIFIED_BIT+1));
b &= flags;
switch (b) {
case 0: return ModifiedEnum.NONE; // 00000
case 8: return ModifiedEnum.INSERTED; // 01000
case 16: return ModifiedEnum.REMOVED; // 10000
case 24: return ModifiedEnum.UPDATED; // 11000
}
throw new IllegalStateException();
}
/**
* Modified enum is hiding in the 3 and 4 bits of the flags.
*/
private void modified(final ModifiedEnum modified) {
byte b = flags;
if (modified == null) {
throw new IllegalArgumentException();
} else {
switch(modified) {
case NONE:
b = Bits.set(Bits.set(b, MODIFIED_BIT, false), (MODIFIED_BIT+1), false);
break;
case INSERTED:
b = Bits.set(Bits.set(b, MODIFIED_BIT, true), (MODIFIED_BIT+1), false);
break;
case REMOVED:
b = Bits.set(Bits.set(b, MODIFIED_BIT, false), (MODIFIED_BIT+1), true);
break;
case UPDATED:
b = Bits.set(Bits.set(b, MODIFIED_BIT, true), (MODIFIED_BIT+1), true);
break;
default:
throw new IllegalStateException();
}
}
flags = b;
}
/**
* User flag is hiding in the 5 bit of the flags.
*/
private boolean userFlag() {
return Bits.get(flags, USERFLAG_BIT);
}
/**
* User flag is hiding in the 5 bit of the flags.
*/
private void userFlag(final boolean userFlag) {
flags = Bits.set(flags, USERFLAG_BIT, userFlag);
}
/**
* Override is hiding in the 6 bit of the flags.
*/
private boolean override() {
return Bits.get(flags, OVERRIDE_BIT);
}
/**
* Override is hiding in the 6 bit of the flags.
*/
private void override(final boolean override) {
flags = Bits.set(flags, OVERRIDE_BIT, override);
}
// /**
// * Sidable is hiding in the 7 bit of the flags.
// */
// private boolean sidable() {
// return Bits.get(flags, SIDABLE_BIT);
// }
//
// /**
// * Sidable is hiding in the 7 bit of the flags.
// */
// private void sidable(final boolean sidable) {
// flags = Bits.set(flags, SIDABLE_BIT, sidable);
// }
/**
* {@inheritDoc}
*
* Note: This methods rely on the fact that IV implements Value. The
* returned IV will act like a Value if it is inline or if it has been
* materialized. If the IV is not-inline and its value has not been
* materialized from the appropriate index or explicitly set through
* {@link IVCache#setValue(BigdataValue)}, then an attempt to access the
* state of the {@link Value} through its {@link Value} interface will throw
* a {@link NotMaterializedException}.
*/
@Override
public Resource getContext() {
return (Resource) c();
}
/**
* {@inheritDoc}
*
* Note: This methods rely on the fact that IV implements Value. The
* returned IV will act like a Value if it is inline or if it has been
* materialized. If the IV is not-inline and its value has not been
* materialized from the appropriate index or explicitly set through
* {@link IVCache#setValue(BigdataValue)}, then an attempt to access the
* state of the {@link Value} through its {@link Value} interface will throw
* a {@link NotMaterializedException}.
*/
@Override
public Value getObject() {
return (Value) o();
}
/**
* {@inheritDoc}
*
* Note: This methods rely on the fact that IV implements Value. The
* returned IV will act like a Value if it is inline or if it has been
* materialized. If the IV is not-inline and its value has not been
* materialized from the appropriate index or explicitly set through
* {@link IVCache#setValue(BigdataValue)}, then an attempt to access the
* state of the {@link Value} through its {@link Value} interface will throw
* a {@link NotMaterializedException}.
*/
@Override
public URI getPredicate() {
return (URI) p();
}
/**
* {@inheritDoc}
*
* Note: This methods rely on the fact that IV implements Value. The
* returned IV will act like a Value if it is inline or if it has been
* materialized. If the IV is not-inline and its value has not been
* materialized from the appropriate index or explicitly set through
* {@link IVCache#setValue(BigdataValue)}, then an attempt to access the
* state of the {@link Value} through its {@link Value} interface will throw
* a {@link NotMaterializedException}.
*/
@Override
public Resource getSubject() {
return (Resource) s();
}
}