blob: 21ffe8e11a047559d11a87090860341232f6276d [file] [log] [blame]
/*
* 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;
import java.io.IOException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.Objects;
import java.util.function.Function;
import org.apache.jena.graph.Node ;
import org.apache.jena.graph.NodeFactory ;
import org.apache.jena.graph.Triple ;
import org.apache.jena.riot.system.Serializer;
public class Quad implements Serializable
{
// Create QuadNames? GraphNames?
/** Name of the default for explicit use in GRAPH */
public static final Node defaultGraphIRI = NodeFactory.createURI("urn:x-arq:DefaultGraph") ;
/** Name of the default graph as used by parsers and in quad form of algebra.
* Not for access to the default graph by name - use Quad.defaultGraphIRI.
*/
public static final Node defaultGraphNodeGenerated = NodeFactory.createURI("urn:x-arq:DefaultGraphNode") ;
/** Name of the merge of all named graphs (use this for the graph of all named graphs) */
public static final Node unionGraph = NodeFactory.createURI("urn:x-arq:UnionGraph") ;
/** Name of the non-graph when a quad is really a triple - also parsing of triples formats
* (and the default graph when parsing N-Quads or TriG)
* Not for access to the default graph by name - use Quad.defaultGraphIRI.
*/
public static final Node tripleInQuad = null ;
/** A {@code Quad} that has a wildcard in all fields. */
public static final Quad ANY = Quad.create( Node.ANY, Node.ANY, Node.ANY, Node.ANY );
private final Node graph, subject, predicate, object ;
public Quad(Node graph, Triple triple)
{
this(graph, triple.getSubject(), triple.getPredicate(), triple.getObject()) ;
}
public Quad(Node g, Node s, Node p, Node o)
{
// Null means it's a triple really.
// if ( g == null )
// throw new UnsupportedOperationException("Quad: graph cannot be null");
if ( s == null )
throw new UnsupportedOperationException("Quad: subject cannot be null");
if ( p == null )
throw new UnsupportedOperationException("Quad: predicate cannot be null");
if ( o == null )
throw new UnsupportedOperationException("Quad: object cannot be null");
this.graph = g ;
this.subject = s ;
this.predicate = p ;
this.object = o ;
}
public static Quad create(Node g, Node s, Node p, Node o) { return new Quad(g,s,p,o) ; }
public static Quad create(Node g, Triple t) { return new Quad(g,t) ; }
public final Node getGraph() { return graph ; }
public final Node getSubject() { return subject ; }
public final Node getPredicate() { return predicate ; }
public final Node getObject() { return object ; }
/** Get as a triple - useful because quads often come in blocks for the same graph */
public Triple asTriple()
{
return new Triple(subject, predicate, object) ;
}
public boolean isConcrete()
{
return subject.isConcrete() && predicate.isConcrete() && object.isConcrete() && graph.isConcrete() ;
}
/** Test whether this is a quad for the default graph (not the default graphs by explicit name) */
public static boolean isDefaultGraphGenerated(Node node)
{
// The node used by the quad generator for the default graph
// Not the named graph that refers to the default graph.
return defaultGraphNodeGenerated.equals(node) ;
}
/** Default, explicitly named concrete graph */
public static boolean isDefaultGraphExplicit(Node node)
{
return defaultGraphIRI.equals(node) ;
}
/** Default, concrete graph (either generated or explicitly named) -- not triple-in-quad*/
public static boolean isDefaultGraph(Node node)
{
return isDefaultGraphGenerated(node) || isDefaultGraphExplicit(node) ;
}
/** Default, concrete graph (either generated or explicitly named) -- not triple-in-quad*/
public static boolean isUnionGraph(Node node)
{
return unionGraph.equals(node) ;
}
/** Default, concrete graph via generated URI (not explicitly, named) */
public boolean isDefaultGraphExplicit()
{ return isDefaultGraphExplicit(getGraph()) ; }
/** Default graph, explicitly named (not generated) */
public boolean isDefaultGraphGenerated()
{
return isDefaultGraphGenerated(getGraph()) ;
}
/** Default, concrete graph (either generated or explicitly named) */
public boolean isDefaultGraph()
{
return isDefaultGraph(getGraph()) ;
}
/** node used for the RDF merge of named graphs */
// public static boolean isQuadUnionGraph(Node node)
// {
// return node.equals(unionGraph) ;
// }
//
public boolean isUnionGraph() { return isUnionGraph(graph) ; }
/** Is it really a triple? */
public boolean isTriple() { return Objects.equals(graph, tripleInQuad) ; }
/** Is this quad a legal data quad (legal data triple, IRI for graph) */
public boolean isLegalAsData()
{
Node sNode = getSubject() ;
Node pNode = getPredicate() ;
Node oNode = getObject() ;
Node gNode = getGraph() ;
if ( sNode.isLiteral() || sNode.isVariable() )
return false ;
if ( ! pNode.isURI() ) // Not variable, literal or blank.
return false ;
if ( oNode.isVariable() )
return false ;
if ( gNode != null ) {
if ( ! gNode.isURI() && ! gNode.isBlank() )
return false ;
}
return true ;
}
// ---- Serializable
protected Object writeReplace() throws ObjectStreamException {
Function<Quad, Object> function = Serializer.getQuadSerializer() ;
if ( function == null )
throw new IllegalStateException("Function for Quad.writeReplace not set") ;
return function.apply(this);
}
// Any attempt to serialize without replacement is an error.
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
throw new IllegalStateException();
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
throw new IllegalStateException();
}
// ---- Serializable
@Override
public int hashCode()
{
int x =
(subject.hashCode() >> 1) ^
predicate.hashCode() ^
(object.hashCode() << 1);
if ( graph != null )
x ^= (graph.hashCode()>>2) ;
else
x++ ;
return x ;
}
@Override
public boolean equals(Object other)
{
if ( this == other ) return true ;
if ( ! ( other instanceof Quad) )
return false ;
Quad quad = (Quad)other ;
if ( ! Objects.equals(graph, quad.graph) ) return false ;
if ( ! subject.equals(quad.subject) ) return false ;
if ( ! predicate.equals(quad.predicate) ) return false ;
if ( ! object.equals(quad.object) ) return false ;
return true ;
}
public boolean matches(Node g, Node s, Node p, Node o)
{
return nodeMatches(getGraph(), g) && nodeMatches(getSubject(), s) &&
nodeMatches(getPredicate(), p) && nodeMatches(getObject(), o) ;
}
private static boolean nodeMatches(Node thisNode, Node otherNode)
{
// otherNode may be Node.ANY, and this works out.
return otherNode.matches(thisNode) ;
}
@Override
public String toString()
{
String str = (graph==null)?"_":graph.toString() ;
return "["+str+" "+subject.toString()+" "+predicate.toString()+" "+object.toString()+"]" ;
}
}