com.bigdata.rdf.spo.SPOKeyOrder 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 Jan 26, 2007
*/
package com.bigdata.rdf.spo;
import java.io.Externalizable;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.apache.log4j.Logger;
import com.bigdata.bop.IConstant;
import com.bigdata.bop.IPredicate;
import com.bigdata.bop.IVariableOrConstant;
import com.bigdata.btree.keys.IKeyBuilder;
import com.bigdata.btree.keys.SuccessorUtil;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.IVUtility;
import com.bigdata.rdf.internal.constraints.RangeBOp;
import com.bigdata.rdf.model.StatementEnum;
import com.bigdata.rdf.sparql.ast.eval.AST2BOpBase;
import com.bigdata.striterator.AbstractKeyOrder;
/**
* Represents the key order used by an index for a triple relation.
*
* @serial The serialization of the class is quite small since the only instance
* field is {@link #index()}. All other data are static. However, it is
* still MUCH more efficient to only transmit the {@link #index()} byte
* without the overhead of the class metadata, which is an additional
* 60 bytes! Classes embedding serialized
* {@link SPOKeyOrder} are strongly encouraged to make this
* optimization.
*
* @author Bryan Thompson
*/
public class SPOKeyOrder extends AbstractKeyOrder implements Serializable {
/**
*
*/
private static final long serialVersionUID = 87501920529732159L;
private static final transient Logger log = Logger.getLogger(SPOKeyOrder.class);
private static final boolean DEBUG = log.isDebugEnabled();
/**
* Enables or disables the index locality optimization.
*
* @see #getKeyOrder(IPredicate, int)
* @see
* Choosing the index for testing fully bound access paths based on
* index locality
*/
private static final transient boolean LOCALITY_OPTIMIZATION = false;
/*
* Note: these constants make it possible to use switch(index()) constructs.
*/
// triple store indices.
public static final transient int _SPO = 0;
public static final transient int _OSP = 1;
public static final transient int _POS = 2;
// quad store indices.
public static final transient int _SPOC = 3;
public static final transient int _POCS = 4;
public static final transient int _OCSP = 5;
public static final transient int _CSPO = 6;
public static final transient int _PCSO = 7;
public static final transient int _SOPC = 8;
public static final transient int FIRST_TRIPLE_INDEX = _SPO;
public static final transient int LAST_TRIPLE_INDEX = _POS;
public static final transient int FIRST_QUAD_INDEX = _SPOC;
public static final transient int LAST_QUAD_INDEX = _SOPC;
public static final transient int MAX_INDEX_COUNT = 9;
/*
* The three perfect natural orders for triples.
*/
public static final transient SPOKeyOrder SPO = new SPOKeyOrder(_SPO);
public static final transient SPOKeyOrder POS = new SPOKeyOrder(_POS);
public static final transient SPOKeyOrder OSP = new SPOKeyOrder(_OSP);
/*
* The six perfect natural orders for quads.
*/
public static final transient SPOKeyOrder SPOC = new SPOKeyOrder(_SPOC);
public static final transient SPOKeyOrder POCS = new SPOKeyOrder(_POCS);
public static final transient SPOKeyOrder OCSP = new SPOKeyOrder(_OCSP);
public static final transient SPOKeyOrder CSPO = new SPOKeyOrder(_CSPO);
public static final transient SPOKeyOrder PCSO = new SPOKeyOrder(_PCSO);
public static final transient SPOKeyOrder SOPC = new SPOKeyOrder(_SOPC);
/**
* The name for each of the natural key orders.
*/
static final transient String[] names = new String[] {
// triples
"SPO",//
"OSP",//
"POS",//
// quads
"SPOC",//
"POCS",//
"OCSP",//
"CSPO",//
"PCSO",//
"SOPC" //
};
static final transient SPOKeyOrder[] values = new SPOKeyOrder[] {
// triples
SPO,
OSP,
POS,
// quads
SPOC,
POCS,
OCSP,
CSPO,
PCSO,
SOPC,
};
static final transient GeneralComparator[] comparators = new GeneralComparator[] {
// triples
new GeneralComparator(_SPO),
new GeneralComparator(_OSP),
new GeneralComparator(_POS),
// quads
new GeneralComparator(_SPOC),//
new GeneralComparator(_POCS),//
new GeneralComparator(_OCSP),//
new GeneralComparator(_CSPO),//
new GeneralComparator(_PCSO),//
new GeneralComparator(_SOPC) };
/*
* Constants corresponding to the columns of the SPO(C) relation.
*/
public final static transient int S = 0;
public final static transient int P = 1;
public final static transient int O = 2;
public final static transient int C = 3;
/**
* The permutation order for the keys for each of the natural key orders.
*/
static final transient int[][] orders = new int[][] {
// triples
new int[] { S, P, O }, //
new int[] { O, S, P }, //
new int[] { P, O, S }, //
// quads
new int[] { S, P, O, C }, //
new int[] { P, O, C, S }, //
new int[] { O, C, S, P }, //
new int[] { C, S, P, O }, //
new int[] { P, C, S, O }, //
new int[] { S, O, P, C }, //
};
/**
* The unique index used to identify this natural order.
*/
private final byte index;
private SPOKeyOrder(final int index) {
this.index = (byte) index;
}
/**
* Return true
if this is the primary index for the relation.
*
* @return true
for {@link #SPO} or {@link #SPOC}. Those are
* the natural orders corresponding to the primary index for a
* triple store (SPO) and a quad store (SPOC) respectively.
*/
final public boolean isPrimaryIndex() {
return this == SPO || this == SPOC;
}
/**
* Returns the singleton corresponding to the index.
*
* @param index
* The index.
*
* @return The singleton {@link SPOKeyOrder} having that index.
*
* @throws IllegalArgumentException
* if the index is not valid.
*/
static public SPOKeyOrder valueOf(final int index) {
return values[index];
// switch(index) {
// case _SPO:
// return SPO;
// case _POS:
// return POS;
// case _OSP:
// return OSP;
// default:
// throw new IllegalArgumentException("Unknown: index" + index);
// }
}
/**
* Covert a name of an {@link SPOKeyOrder} into an {@link SPOKeyOrder}.
*
* @param name
* The name.
*
* @return The {@link SPOKeyOrder}.
*
* @throws IllegalArgumentException
* if name is not a known {@link SPOKeyOrder}.
*/
public static SPOKeyOrder fromString(final String name) {
for (int i = 0; i < names.length; i++) {
if (names[i].equals(name)) {
return valueOf(i);
}
}
throw new IllegalArgumentException("name=" + name);
}
/**
* The base name for the index.
*/
@Override
final public String getIndexName() {
return names[index];
}
/**
* Return {@link #getIndexName()}'s value.
*/
@Override
public String toString() {
return names[index];
}
/**
* Return either 3 or 4 depending on the #of components in the key for
* this natural key ordering.
*/
@Override
final public int getKeyArity() {
switch (index) {
case _SPO:
case _POS:
case _OSP:
return 3;
case _SPOC:
case _POCS:
case _OCSP:
case _CSPO:
case _PCSO:
case _SOPC:
return 4;
default:
throw new AssertionError();
}
}
/**
* Return the index of the slot in the {@link ISPO} tuple which appears at
* the specified position in the key.
*
* @param keyPos
* The index into the key that is being generated.
*
* @return The index of the slot in the {@link ISPO}.
*/
@Override
final public int getKeyOrder(final int keyPos) {
return orders[index][keyPos];
}
/**
* Returns the position of the given spoIdentifier in the index. For
* instance, if the index is POS and spoIdentfier==0 (representing the
* subject, then 2 is returned (since the subject shows up in index position
* 2 in POS).
*
* @param spoIdentifier identifier, either being 0=subject, 1=predicate,
* 2=object, or 3=context
* @return the position in the key or -1 if not matchable (e.g., if we're
* in triples mode and request the index for c)
*/
final public int getPositionInIndex(final int spoIdentifier) {
final int[] keyOrder = orders[index];
for (int i=0; i getComparator() {
switch (index) {
case _SPO:
return SPOComparator.INSTANCE;
case _POS:
return POSComparator.INSTANCE;
case _OSP:
return OSPComparator.INSTANCE;
case _SPOC:
case _POCS:
case _OCSP:
case _CSPO:
case _PCSO:
case _SOPC:
return comparators[index];
default:
throw new AssertionError();
}
}
/**
* Generalized comparator for {@link ISPO}s.
*/
private static class GeneralComparator implements Comparator {
private final byte index;
public GeneralComparator(final int index) {
this.index = (byte)index;
}
@Override
public int compare(final ISPO o1, final ISPO o2) {
if (o1 == o2) {
return 0;
}
final int[] keyMap = orders[index];
// compare terms one by one in the appropriate key order
for (int i = 0; i < keyMap.length; i++) {
final IV, ?> t1 = o1.get(keyMap[i]);
final IV, ?> t2 = o2.get(keyMap[i]);
final int ret = IVUtility.compare(t1, t2);
if (ret != 0) {
return ret;
}
}
// all terms match
return 0;
}
}
// /**
// * Return the from key for this particular set of known terms that will
// * allow us to read everything about those terms from the index. For
// * example, if this happens to be the SPO key order and the size of known
// * terms is 2, this method will provide a from key that will allow the
// * caller to read all the Os for a particular SP combo.
// *
// * @param knownTerms
// * the known terms
// * @return
// * the from key
// */
// final public byte[] getFromKey(final IKeyBuilder keyBuilder,
// final IV[] knownTerms) {
//
// if (knownTerms == null || knownTerms.length == 0)
// return null;
//
// keyBuilder.reset();
//
// for (int i = 0; i < knownTerms.length; i++)
// knownTerms[i].encode(keyBuilder);
//
// return keyBuilder.getKey();
//
// }
//
// /**
// * Return the to key for this particular set of known terms that will
// * allow us to read everything about those terms from the index. For
// * example, if this happens to be the SPO key order and the size of known
// * terms is 2, this method will provide a to key that will allow the
// * caller to read all the Os for a particular SP combo.
// *
// * @param knownTerms
// * the known terms
// * @return
// * the to key
// */
// final public byte[] getToKey(final IKeyBuilder keyBuilder,
// final IV[] knownTerms) {
//
// if (knownTerms == null || knownTerms.length == 0)
// return null;
//
// final byte[] from = getFromKey(keyBuilder, knownTerms);
//
// return SuccessorUtil.successor(from);
//
// }
// /**
// * Return the inclusive lower bound which would be used for a query against
// * this {@link IKeyOrder} for the given {@link IPredicate}.
// *
// * @todo This method should be declared by {@link IKeyOrder}.
// */
// final public byte[] getFromKey(final IKeyBuilder keyBuilder,
// final IPredicate predicate) {
//
// keyBuilder.reset();
//
// final int keyArity = getKeyArity(); // use the key's "arity".
//
// boolean noneBound = true;
//
// for (int i = 0; i < keyArity; i++) {
//
// final IVariableOrConstant term = predicate.get(getKeyOrder(i));
//
// // Note: term MAY be null for the context position.
// if (term == null || term.isVar())
// break;
//
// final IV iv = term.get();
//
// iv.encode(keyBuilder);
//
// noneBound = false;
//
// }
//
// return noneBound ? null : keyBuilder.getKey();
//
// }
/**
* {@inheritDoc}
*
* TODO I think that we should just rely on the correct attachment of the
* range constraint filters to the SPs (and the propagation of the
* constraints to the IPredicates) rather than doing dynamic attachment
* here. Static analysis can determine when the variable will become bound,
* whether in the scope of a required join group or an optional join group.
* (In the case of the optional join group, the joins in the group are still
* required joins, it is just that the solutions outside of the group will
* not have a binding for the variable unless it became bound within the
* optional group. However, this does not change how we attach the range
* constraints to the SPs since a range constraint which is lifted onto the
* AP can only be applied when the variable would become bound by that AP.
* If the variable is already bound, it is of no consequence. The only
* wrinkle would be when one optional group MIGHT have already bound a
* variable and then another optional group which could also bind the same
* variable could observe either a bound variable or an unbound variable.
* The FILTER would have to be in place in both optional groups and the
* range constraint would be lifted onto the access path in both groups.)
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/238 (lift range
* constraints onto AP).
*/
@Override
public byte[] getFromKey(final IKeyBuilder keyBuilder,
final IPredicate predicate) {
keyBuilder.reset();
final int keyArity = getKeyArity(); // use the key's "arity".
boolean noneBound = true;
final RangeBOp range = getRange(predicate);
for (int i = 0; i < keyArity; i++) {
final int index = getKeyOrder(i);
final IVariableOrConstant> term = predicate.get(index);
if (term == null || term.isVar()) {
if (index == 2 && range != null && range.isFromBound()) {
/*
* We are on the O term, it's a variable, and we have a
* lower bound for it.
*/
final IConstant c = (IConstant) range.from();
appendKeyComponent(keyBuilder, i, c.get());
noneBound = false;
if (log.isInfoEnabled()) {
log.info("used range to build to key: " + c);
log.info(predicate);
}
} else {
break;
}
} else {
appendKeyComponent(keyBuilder, i, term.get());
noneBound = false;
}
}
final byte[] key = noneBound ? null : keyBuilder.getKey();
return key;
}
/*
* Ranges are only useful when O is not bound and either SP bound or just P
* SP?: SPO index
* ?P?: POS index
* S??: SPO index, range is not useful without an SOP index
* ???: SPO index, range is not useful unless we switch to OSP
*
* This reduces to when O == null && P != null
*/
private RangeBOp getRange(final IPredicate predicate) {
final RangeBOp range = (RangeBOp)
predicate.getProperty(IPredicate.Annotations.RANGE);
if (range == null)
return null;
// final IVariableOrConstant> s = predicate.get(0);
final IVariableOrConstant> p = predicate.get(1);
final IVariableOrConstant> o = predicate.get(2);
if ((o == null || o.isVar()) && (p != null && p.isConstant())) {
return range;
} else {
return null;
}
}
/**
* {@inheritDoc}
*
* TODO See my notes on getFromKey(). The issue is exactly the same for the
* toKey and fromKey.
*
* TODO Each GT(E)/LT(E) constraint should be broken down into a separate
* filter so we can apply one even when the other might depend on a variable
* which is not yet bound.
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/238 (lift range
* constraints onto AP).
*/
@Override
public byte[] getToKey(final IKeyBuilder keyBuilder,
final IPredicate predicate) {
keyBuilder.reset();
final int keyArity = getKeyArity(); // use the key's "arity".
boolean noneBound = true;
final RangeBOp range = getRange(predicate);
for (int i = 0; i < keyArity; i++) {
final int index = getKeyOrder(i);
final IVariableOrConstant> term = predicate.get(index);
// Note: term MAY be null for the context position.
if (term == null || term.isVar()) {
if (index == 2 && range != null && range.isToBound()) {
/*
* We are on the O term, it's a variable, and we have an
* upper bound for it.
*/
final IConstant c = (IConstant) range.to();
appendKeyComponent(keyBuilder, i, c.get());
noneBound = false;
if (log.isInfoEnabled()) {
log.info("used range to build to key: " + c);
log.info(predicate);
}
} else {
break;
}
} else {
appendKeyComponent(keyBuilder, i, term.get());
noneBound = false;
}
}
final byte[] key = noneBound ? null : keyBuilder.getKey();
return key == null ? null : SuccessorUtil.successor(key);
}
@Override
protected void appendKeyComponent(final IKeyBuilder keyBuilder,
final int index, final Object keyComponent) {
((IV) keyComponent).encode(keyBuilder);
// log.debug("appending key component: " + keyComponent);
}
// /**
// * Return the exclusive upper bound which would be used for a query against
// * this {@link IKeyOrder} for the given {@link IPredicate}.
// *
// * @todo This method should be declared by {@link IKeyOrder}.
// */
// final public byte[] getToKey(final IKeyBuilder keyBuilder,
// final IPredicate predicate) {
//
// final byte[] from = getFromKey(keyBuilder, predicate);
//
// return from == null ? null : SuccessorUtil.successor(from);
//
// }
/**
* Forms the key for a given index order (the {@link SPOTupleSerializer}
* delegates its behavior to this method).
*
* Note: The {@link IKeyBuilder} is {@link IKeyBuilder#reset()} by this
* method.
*
* @param keyBuilder
* The key builder.
* @param spo
* The {@link ISPO}.
* @return The encoded key.
*
* @see #appendKey(IKeyBuilder, ISPO)
*/
final public byte[] encodeKey(final IKeyBuilder keyBuilder, final ISPO spo) {
keyBuilder.reset();
appendKey(keyBuilder,spo);
return keyBuilder.getKey();
}
/**
* Appends the key for a given index order into the {@link IKeyBuilder}.
*
* Note: The {@link IKeyBuilder} is NOT {@link IKeyBuilder#reset()} by this
* method.
*
* @param keyBuilder
* The key builder - this is NOT reset().
* @param spo
* The {@link ISPO}.
*
* @return The {@link IKeyBuilder}.
*
* @see #appendKey(IKeyBuilder, ISPO)
*/
final public IKeyBuilder appendKey(final IKeyBuilder keyBuilder, final ISPO spo) {
final int[] a = orders[index];
for (int i = 0; i < a.length; i++) {
IVUtility.encode(keyBuilder, spo.get(a[i]));
}
return keyBuilder;
}
/**
* Decode the key into an {@link SPO}. The {@link StatementEnum} and the
* optional SID will not be decoded, since it is carried in the B+Tree
* value. However, if the {@link SPOKeyOrder} is a quad order then the
* {@link SPO#c()} will be bound.
*
* @param key
* The key.
*
* @return The decoded key.
*/
final public SPO decodeKey(final byte[] key) {
return decodeKey(key, 0 /* offset */);
}
/**
* Decode the key into an {@link SPO}. The {@link StatementEnum} and the
* optional SID will not be decoded, since it is carried in the B+Tree
* value. However, if the {@link SPOKeyOrder} is a quad order then the
* {@link SPO#c()} will be bound.
*
* @param key
* The key.
* @param offset
* The offset into the key.
*
* @return The decoded key.
*/
final public SPO decodeKey(final byte[] key, final int offset) {
/*
* Note: GTE since the key is typically a reused buffer which may be
* larger than the #of bytes actually holding valid data.
*/
final int keyArity = getKeyArity();
final IV[] ivs = IVUtility.decode(key, offset, keyArity);
if (DEBUG) {
log.debug("key: " + Arrays.toString(key));
log.debug("keyArity: " + keyArity);
log.debug(Arrays.toString(ivs));
}
final IV _0 = ivs[0];
final IV _1 = ivs[1];
final IV _2 = ivs[2];
// 4th key position exists iff quad keys.
final IV _3 = keyArity == 4 ? ivs[3] : null;
/*
* Re-order the key into SPO order.
*/
final IV s, p, o, c;
switch (index) {
/*
* Triples
*
* [c] will be NULL for triples, but the SID may be read from the value
* associated with the key below and set on the SPO object.
*/
case SPOKeyOrder._SPO:
s = _0;
p = _1;
o = _2;
c = null;
break;
case SPOKeyOrder._POS:
p = _0;
o = _1;
s = _2;
c = null;
break;
case SPOKeyOrder._OSP:
o = _0;
s = _1;
p = _2;
c = null;
break;
/*
* Quads
*/
case SPOKeyOrder._SPOC:
s = _0;
p = _1;
o = _2;
c = _3;
break;
case SPOKeyOrder._POCS:
p = _0;
o = _1;
c = _2;
s = _3;
break;
case SPOKeyOrder._OCSP:
o = _0;
c = _1;
s = _2;
p = _3;
break;
case SPOKeyOrder._CSPO:
c = _0;
s = _1;
p = _2;
o = _3;
break;
case SPOKeyOrder._PCSO:
p = _0;
c = _1;
s = _2;
o = _3;
break;
case SPOKeyOrder._SOPC:
s = _0;
o = _1;
p = _2;
c = _3;
break;
default:
throw new UnsupportedOperationException();
}
return new SPO(s, p, o, c);
}
/**
* Imposes the canonicalizing mapping during object de-serialization.
*
* Note: implementing {@link Externalizable} drops the serialized size from
* 61 bytes per instance to 56 bytes per instance. On the other hand, if the
* class embedding the {@link SPOKeyOrder} serializes the {@link #index} as
* a byte
, it only take a single byte to serialize each
* instance.
*
* Note: Serialization breaks with the introduction of quads as the
* name
field is no longer serialized and the {@link #index()}
* is serialized as a byte field.
*/
private Object readResolve() throws ObjectStreamException {
return SPOKeyOrder.valueOf(index);
}
/**
* Return the {@link SPOKeyOrder} for the given predicate.
*
* @param predicate
* The predicate.
*
* @return The {@link SPOKeyOrder}
*
* @todo A variant of this method should be raised onto IKeyOrder without
* the keyArity parameter. That parameter is only there because we
* support two distinct families of natural orders in this class: one
* for triples and one for quads.
*
* @see
* Choosing the index for testing fully bound access paths based on
* index locality
*/
static public SPOKeyOrder getKeyOrder(final IPredicate predicate,
final int keyArity) {
/*
* Look for a key order override.
*/
final SPOKeyOrder keyOrder = (SPOKeyOrder) predicate.getKeyOrder();
if (keyOrder != null) {
// Overridden.
return keyOrder;
}
// final RangeBOp range = (RangeBOp)
// predicate.getProperty(IPredicate.Annotations.RANGE);
//
// final boolean rangeIsBound = range != null && range.isFullyBound();
final boolean s = !predicate.get(0).isVar();
final boolean p = !predicate.get(1).isVar();
final boolean o = !predicate.get(2).isVar(); // || rangeIsBound;
if (keyArity == 3) {
// Note: Context is ignored!
if (s && p && o) {
/*
* If the access path is all bound, then we want to use the
* index associated with the original predicate (which typically
* had variables in one or more positions). This index will have
* better locality since it will naturally group the index
* accesses in the same region of the index.
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/150
* (chosing the index for testing fully bound access paths based
* on index locality)
*/
if (LOCALITY_OPTIMIZATION) {
final SPOKeyOrder tmp = predicate.getProperty(
AST2BOpBase.Annotations.ORIGINAL_INDEX, SPO);
return tmp;
} else {
return SPO;
}
} else if (s && p) {
return SPO;
} else if (s && o) {
return OSP;
} else if (p && o) {
return POS;
} else if (s) {
return SPO;
} else if (p) {
return POS;
} else if (o) {
return OSP;
} else {
return SPO;
}
} else {
@SuppressWarnings({ "unchecked", "rawtypes" })
final IVariableOrConstant t = predicate.get(3);
final boolean c = t != null && !t.isVar();
if ((!s && p && !o && !c)
|| (!s && p && o && !c)
|| (!s && p && o && c)) {
return POCS;
}
if ((!s && !p && o && !c)
|| (!s && !p && o && c)
|| (s && !p && o && c)) {
return OCSP;
}
if ((!s && !p && !o && c)
|| (s && !p && !o && c)
|| (s && p && !o && c)) {
return CSPO;
}
if ((!s && p && !o && c)) {
return PCSO;
}
if ((s && !p && o && !c)) {
return SOPC;
}
/*
* If the access path is all bound, then we want to use the
* index associated with the original predicate (which typically
* had variables in one or more positions). This index will have
* better locality since it will naturally group the index
* accesses in the same region of the index.
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/150
* (chosing the index for testing fully bound access paths based
* on index locality)
*/
if (LOCALITY_OPTIMIZATION) {
final SPOKeyOrder tmp = predicate.getProperty(
AST2BOpBase.Annotations.ORIGINAL_INDEX, SPOC);
return tmp;
} else {
return SPOC;
}
}
}
/**
* Iterator visits {@link #SPO}.
*
* @author Bryan Thompson
*/
static private class SPOOnlyKeyOrderIterator implements
Iterator {
boolean exhausted = false;
@Override
public boolean hasNext() {
return !exhausted;
}
@Override
public SPOKeyOrder next() {
if (!hasNext())
throw new NoSuchElementException();
exhausted = true;
return SPOKeyOrder.SPO;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
/**
* Iterator visits {@link #SPOC}.
*
* @author Bryan
* Thompson
*/
static private class SPOCOnlyKeyOrderIterator implements
Iterator {
boolean exhausted = false;
@Override
public boolean hasNext() {
return !exhausted;
}
@Override
public SPOKeyOrder next() {
if (!hasNext())
throw new NoSuchElementException();
exhausted = true;
return SPOKeyOrder.SPOC;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
/**
* The triple store indices.
*/
static private final transient SPOKeyOrder[] tripleStoreIndices = { SPO,
POS, OSP };
/**
* The quad store indices.
*/
static private final transient SPOKeyOrder[] quadStoreIndices = { SPOC,
POCS, OCSP, CSPO, PCSO, SOPC };
/**
* Return an iterator which visits the triple store indices ({@link #SPO},
* {@link #POS}, {@link #OSP}).
*/
static public Iterator tripleStoreKeyOrderIterator() {
return Arrays.asList(tripleStoreIndices).iterator();
}
/**
* Return an iterator which visits the quad store indices ({@link #SPOC},
* {@link #POCS}, {@link #OCSP}, {@link #CSPO}, {@link #PCSO}, {@link #SOPC}
* ).
*/
static public Iterator quadStoreKeyOrderIterator() {
return Arrays.asList(quadStoreIndices).iterator();
}
/**
* Return an iterator which visits only {@link #SPO}.
*/
static public Iterator spoOnlyKeyOrderIterator() {
return new SPOOnlyKeyOrderIterator();
}
/**
* Return an iterator which visits only {@link #SPOC}.
*/
static public Iterator spocOnlyKeyOrderIterator() {
return new SPOCOnlyKeyOrderIterator();
}
}