/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com 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.internal.impl.bnode; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.ObjectStreamException; import java.io.Serializable; import java.math.BigInteger; import org.openrdf.model.BNode; 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.DTE; import com.bigdata.rdf.internal.ILexiconConfiguration; import com.bigdata.rdf.internal.IV; import com.bigdata.rdf.internal.IVUtility; import com.bigdata.rdf.internal.VTE; import com.bigdata.rdf.internal.impl.AbstractIV; import com.bigdata.rdf.internal.impl.AbstractInlineIV; import com.bigdata.rdf.lexicon.LexiconRelation; import com.bigdata.rdf.model.BigdataBNode; import com.bigdata.rdf.model.BigdataResource; import com.bigdata.rdf.model.BigdataURI; import com.bigdata.rdf.model.BigdataValue; import com.bigdata.rdf.model.BigdataValueFactory; import com.bigdata.rdf.model.StatementEnum; import com.bigdata.rdf.spo.ISPO; import com.bigdata.rdf.spo.SPO; import com.bigdata.rdf.spo.SPOComparator; import com.bigdata.rdf.spo.SPOKeyOrder; /** * Internal value representing an inline statement identifier. Uses the * {@link ISPO} supplied in the ctor as the inline value. The * {@link #asValue(BigdataValueFactory, ILexiconConfiguration)} method returns a * {@link BigdataBNode} that is used to represent the sid in serialization * formats (such as the custom RDF/XML extension for sids). The bnode is * guaranteed to always have the same bnode id for a given inlined SPO. This is * accomplished using the byte[] key encoding for the spo along with the * BigInteger class. * <p> * This internal value has a {@link VTE} of {@link VTE#STATEMENT}. It is encoded * into the statement indices by directly encoding the spo using * {@link SPOKeyOrder#encodeKey(IKeyBuilder, ISPO)} via the * {@link SPOKeyOrder#SPO} key order. Thus when decoded from the statement * indices, the spo associated with this sid is materialized directly from the * sid itself. See {@link IVUtility#decode(byte[])}. The spo decoded from the * sid IV will be marked as explicit (only explicit statements have sids) and * this SidIV will be attached to it. This completely eliminates the need for a * reverse index from sid->spo, as the spo is encoded inline into the SidIV * itself. This replaces the TermId model for representing sids. * <p> * {@inheritDoc} */ public class SidIV<V extends BigdataBNode> extends AbstractInlineIV<V, ISPO> implements Serializable, BNode { /** * */ private static final long serialVersionUID = 685148537376856907L; // private static final transient Logger log = Logger.getLogger(SidIV.class); /** * The inline spo. */ private final ISPO spo; /** * The cached byte[] key for the encoding of this IV. */ private transient byte[] key; /** * The cached materialized BigdataValue for this sid. */ private transient V bnode; @Override public IV<V, ISPO> clone(final boolean clearCache) { final SidIV<V> tmp = new SidIV<V>(spo); // Propagate the cached byte[] key. tmp.key = key; // Propagate the cached BigdataValue. tmp.bnode = bnode; if (!clearCache) { tmp.setValue(getValueCache()); } return tmp; } /** * Ctor with internal value spo specified. */ public SidIV(final ISPO spo) { /* * Note: XSDBoolean happens to be assigned the code value of 0, which is * the value we we want when the data type enumeration will be ignored. */ super(VTE.STATEMENT, DTE.XSDBoolean); this.spo = spo; } /** * Return the <code>flags</code> byte for a {@link SidIV}. */ public static final byte toFlags() { /* * Note: XSDBoolean happens to be assigned the code value of 0, which is * the value we want when the data type enumeration will be ignored. */ return AbstractIV.toFlags(VTE.STATEMENT, true/* inline */, false/* extension */, DTE.XSDBoolean); } /** * Returns the inline spo. */ @Override public ISPO getInlineValue() throws UnsupportedOperationException { return spo; } /** * Returns the bnode representation of this IV, useful for serialization * formats such as RDF/XML. See {@link #bnodeId()}. */ @SuppressWarnings("unchecked") @Override public V asValue(final LexiconRelation lex) { if (bnode == null) { bnode = (V) lex.getValueFactory().createBNode(getID()); bnode.setIV(this); bnode.setStatementIdentifier(true); final BigdataResource c = spo.c() != null ? (BigdataResource) spo.c().asValue(lex) : null; bnode.setStatement(lex.getValueFactory().createStatement( (BigdataResource) spo.s().asValue(lex), (BigdataURI) spo.p().asValue(lex), (BigdataValue) spo.o().asValue(lex), c)); } return bnode; } /** * Return the byte length for the byte[] encoded representation of this * internal value. Depends on the byte length of the encoded inline spo. */ @Override public int byteLength() { return 1 + key().length; } @Override public String toString() { return "Sid("+toString(spo)+")"; } /** * Pretty print the inline spo. Calling SPO.toString() results in an * infinite loop. */ private static String toString(final ISPO spo) { return (SPO.toString(spo.s()) + ":" + SPO.toString(spo.p()) + ":" + SPO.toString(spo.o())); } @Override public int hashCode() { return spo.hashCode(); } /** * Implements {@link BNode#getID()}. * <p> * This implementation uses the {@link BigInteger} class to create a unique * blank node ID based on the <code>unsigned byte[]</code> key of the inline * {@link SPO}. */ @Override public String getID() { // // just use the hash code. can result in collisions // return String.valueOf(hashCode()); // create a big integer using the spo key. should result in unique ids final byte[] key = key(); final int signum = key.length > 0 ? 1 : 0; final BigInteger bi = new BigInteger(signum, key); return 's' + bi.toString(); // return toString(); } /** * Two {@link SidIV} are equal if their (s,p,o) IVs are equal. */ @Override public boolean equals(final Object o) { if (this == o) return true; if (o instanceof SidIV) { final ISPO stmt2 = ((SidIV<?>) o).spo; return IVUtility.equals(spo.s(), stmt2.s()) && // IVUtility.equals(spo.p(), stmt2.p()) && // IVUtility.equals(spo.o(), stmt2.o())// ; // return spo.equals(spo2); } return false; } public int _compareTo(final IV o) { /* * Note: This works, but it might be more expensive. */ // return UnsignedByteArrayComparator.INSTANCE.compare(key(), ((SidIV)o).key()); /* * This should work as soon as we fix the other IV Comparable * implementations. */ final ISPO spo2 = ((SidIV) o).spo; return SPOComparator.INSTANCE.compare(spo, spo2); } /** * Encode this internal value into the supplied key builder. Emits the * flags, following by the encoded byte[] representing the spo, in SPO * key order. * <p> * {@inheritDoc} */ @Override public IKeyBuilder encode(final IKeyBuilder keyBuilder) { // First emit the flags byte. keyBuilder.appendSigned(flags()); // Then append the SPO's key in SPOKeyOrder.SPO keyBuilder.append(key()); return keyBuilder; } private byte[] key() { if (key == null) { /* * Build the SPO's key in SPOKeyOrder.SPO. */ final IKeyBuilder keyBuilder = new KeyBuilder(64/* initialCapacity */); key = SPOKeyOrder.SPO.encodeKey(keyBuilder, spo); } return key; } /** * Object provides serialization for {@link SidIV} via the write-replace * and read-replace pattern. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> */ private static class SidIVState implements Externalizable { private static final long serialVersionUID = -1L; // private byte flags; private byte[] key; /** * De-serialization constructor. */ public SidIVState() { } private SidIVState(final SidIV iv) { // this.flags = flags; this.key = iv.key(); } @Override public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { // flags = in.readByte(); final int nbytes = LongPacker.unpackInt(in); key = new byte[nbytes]; in.readFully(key); } @Override public void writeExternal(final ObjectOutput out) throws IOException { // out.writeByte(flags); LongPacker.packLong(out, key.length); out.write(key); } private Object readResolve() throws ObjectStreamException { final ISPO spo = SPOKeyOrder.SPO.decodeKey(key); // SIDs are always explicit statements. spo.setStatementType(StatementEnum.Explicit); return new SidIV(spo); } } private Object writeReplace() throws ObjectStreamException { return new SidIVState(this); } /** * Implements {@link Value#stringValue()}. */ @Override public String stringValue() { return getID(); } /** * Does not need materialization to answer BNode interface methods. */ @Override public boolean needsMaterialization() { return false; } }