/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.sparql.core.mem;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.EnumSet.copyOf;
import static org.apache.jena.graph.Node.ANY;
import static org.apache.jena.sparql.core.mem.TupleSlot.*;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Triple;
import org.apache.jena.sparql.core.Quad;
/**
* Six covering table forms and machinery to determine which of them is best suited to answer a given query. Please
* notice that the individual values of this {@code enum} are what implement the various interfaces named in the
* signature of this type. In particular, any value from this {@code enum} is a complete implementation of
* {@link QuadTable}. {@link HexTable} binds up all these six forms into a single implementation of {@code QuadTable}
* that selects the most useful table form(s) for any given operation.
*
* @see HexTable
*/
public enum QuadTableForm implements Supplier<QuadTable>,Predicate<Set<TupleSlot>> {
/**
* Graph-subject-predicate-object.
*/
GSPO(asList(GRAPH, SUBJECT, PREDICATE, OBJECT)) {
@Override
public PMapQuadTable get() {
return new PMapQuadTable(name()) {
@Override
public Stream<Node> listGraphNodes() {
return local().get().entryStream().map(Entry::getKey);
}
};
}
},
/**
* Graph-object-predicate-subject.
*/
GOPS(asList(GRAPH, OBJECT, PREDICATE, SUBJECT)),
/**
* Subject-predicate-object-graph.
*/
SPOG(asList(SUBJECT, PREDICATE, OBJECT, GRAPH)) {
@Override
public PMapQuadTable get() {
return new PMapQuadTable(name()) {
@Override
public Stream<Quad> findInUnionGraph(final Node s, final Node p, final Node o) {
final AtomicReference<Triple> mostRecentlySeen = new AtomicReference<>();
return find(ANY, s, p, o).map(Quad::asTriple).filter(t->{
return !mostRecentlySeen.getAndSet(t).equals(t);
}).map(t->Quad.create(Quad.unionGraph, t)) ;
}
};
}
},
/**
* Object-subject-graph-predicate.
*/
OSGP(asList(OBJECT, SUBJECT, GRAPH, PREDICATE)),
/**
* Predicate-graph-subject-object.
*/
PGSO(asList(PREDICATE, GRAPH, SUBJECT, OBJECT)),
/**
* Object-predicate-subject-graph.
*/
OPSG(asList(OBJECT, PREDICATE, SUBJECT, GRAPH)) {
@Override
public PMapQuadTable get() {
return new PMapQuadTable(name()) {
@Override
public Stream<Quad> findInUnionGraph(final Node s, final Node p, final Node o) {
final AtomicReference<Triple> mostRecentlySeen = new AtomicReference<>();
return find(ANY, s, p, o).map(Quad::asTriple).filter(t->{
return !mostRecentlySeen.getAndSet(t).equals(t);
}).map(t->Quad.create(Quad.unionGraph, t)) ;
}
};
}
};
@Override
public PMapQuadTable get() {
return new PMapQuadTable(name());
}
private QuadTableForm(final List<TupleSlot> fp) {
this.fullpattern = fp;
}
/**
* The full pattern of this table form.
*/
public final List<TupleSlot> fullpattern;
/**
* @param pattern
* @return whether this table form avoids traversal for a query of this pattern
*/
@Override
public boolean test(final Set<TupleSlot> pattern) {
for (byte i = 4; i > 0; i--) {
// copy into a set because order does not matter for this comparison: the ordering of tuples is
// handled by individual table forms
final Set<TupleSlot> prefix = copyOf(fullpattern.subList(0, i));
if (prefix.equals(pattern)) return true;
}
return false;
}
/**
* @param pattern
* @return the most appropriate choice of table form for that query
*/
public static QuadTableForm chooseFrom(final Set<TupleSlot> pattern) {
return tableForms().filter(f -> f.test(pattern)).findFirst().orElse(GSPO);
}
/**
* @return a stream of these table forms
*/
public static Stream<QuadTableForm> tableForms() {
return stream(values());
}
}