/*
* 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.graph;
import java.util.function.Predicate;
import org.apache.jena.shared.PrefixMapping ;
import org.apache.jena.util.iterator.ExtendedIterator ;
import org.apache.jena.util.iterator.NullIterator ;
/**
Triples are the basis for RDF statements; they have a subject, predicate, and
object field (all nodes) and express the notion that the relationship named
by the predicate holds between the subject and the object.
*/
public class Triple
{
private final Node subj, pred, obj;
public Triple( Node s, Node p, Node o )
{
if (s == null) throw new UnsupportedOperationException( "subject cannot be null" );
if (p == null) throw new UnsupportedOperationException( "predicate cannot be null" );
if (o == null) throw new UnsupportedOperationException( "object cannot be null" );
subj = s;
pred = p;
obj = o;
}
/**
A triple-iterator with no elements.
@deprecated Use {@link NullIterator#instance()}
*/
@Deprecated
public static final ExtendedIterator<Triple> None = NullIterator.instance() ;
/**
return a human-readable string "subject @predicate object" describing the triple
*/
@Override
public String toString()
{ return toString( PrefixMapping.Standard ); }
public String toString( PrefixMapping pm )
{
return subj.toString( pm, true )
+ " @" + pred.toString( pm, true )
+ " " + obj.toString( pm, true );
}
/**
@return the subject of the triple
*/
public final Node getSubject()
{ return subj; }
/**
@return the predicate of the triple
*/
public final Node getPredicate()
{ return pred; }
/**
@return the object of the triple
*/
public final Node getObject()
{ return obj; }
/** Return subject or null, not Node.ANY */
public Node getMatchSubject()
{ return anyToNull( subj ); }
/** Return predicate or null, not Node.ANY */
public Node getMatchPredicate()
{ return anyToNull( pred ); }
/** Return object or null, not Node.ANY */
public Node getMatchObject()
{ return anyToNull( obj ); }
private static Node anyToNull( Node n )
{ return Node.ANY.equals( n ) ? null : n; }
private static Node nullToAny( Node n )
{ return n == null ? Node.ANY : n; }
public boolean isConcrete()
{ return subj.isConcrete() && pred.isConcrete() && obj.isConcrete(); }
/**
Answer true if <code>o</code> is a Triple with the same subject, predicate,
and object as this triple.
*/
@Override
public boolean equals(Object o)
{ return o instanceof Triple && ((Triple) o).sameAs( subj, pred, obj ); }
/**
Answer true iff this triple has subject s, predicate p, and object o.
*/
public boolean sameAs( Node s, Node p, Node o )
{ return subj.equals( s ) && pred.equals( p ) && obj.equals( o ); }
/** Does this triple, used as a pattern match, the other triple (usually a ground triple) */
public boolean matches( Triple other )
{ return other.matchedBy( subj, pred, obj ); }
public boolean matches( Node s, Node p, Node o )
{ return subj.matches( s ) && pred.matches( p ) && obj.matches( o ); }
private boolean matchedBy( Node s, Node p, Node o )
{ return s.matches( subj ) && p.matches( pred ) && o.matches( obj ); }
public boolean subjectMatches( Node s )
{ return subj.matches( s ); }
public boolean predicateMatches( Node p )
{ return pred.matches( p ); }
public boolean objectMatches( Node o )
{ return obj.matches( o ); }
/**
The hash-code of a triple is the hash-codes of its components munged
together: see hashCode(S, P, O).
*/
@Override
public int hashCode()
{ return hashCode( subj, pred, obj ); }
/**
Return the munged hashCodes of the specified nodes, an exclusive-or of
the slightly-shifted component hashcodes; this means (almost) all of the bits
count, and the order matters, so (S @P O) has a different hash from
(O @P S), etc.
*/
public static int hashCode( Node s, Node p, Node o )
{ return (s.hashCode() >> 1) ^ p.hashCode() ^ (o.hashCode() << 1); }
public static Triple create( Node s, Node p, Node o )
{
return new Triple( s, p, o ) ;
}
public static Triple createMatch( Node s, Node p, Node o )
{ return Triple.create( nullToAny( s ), nullToAny( p ), nullToAny( o ) ); }
/**
A Triple that is wildcarded in all fields.
*/
public static final Triple ANY = Triple.create( Node.ANY, Node.ANY, Node.ANY );
/**
A Field is a selector from Triples; it allows selectors to be passed
around as if they were functions, hooray.
*/
public static abstract class Field
{
public abstract Node getField( Triple t );
public abstract Predicate<Triple> filterOn( Node n );
public final Predicate<Triple> filterOn( Triple t )
{ return filterOn( getField( t ) ); }
protected static final Predicate<Triple> anyTriple = t -> true;
public static final Field fieldSubject = new Field()
{
@Override public Node getField( Triple t )
{ return t.subj; }
@Override public Predicate<Triple> filterOn( final Node n )
{
return n.isConcrete()
? x -> n.equals( x.subj )
: anyTriple
;
}
};
public static final Field fieldObject = new Field()
{
@Override public Node getField( Triple t )
{ return t.obj; }
@Override public Predicate<Triple> filterOn( final Node n )
{ return n.isConcrete()
? x -> n.sameValueAs( x.obj )
: anyTriple;
}
};
public static final Field fieldPredicate = new Field()
{
@Override public Node getField( Triple t )
{ return t.pred; }
@Override public Predicate<Triple> filterOn( final Node n )
{ return n.isConcrete()
? x -> n.equals( x.pred )
: anyTriple;
}
};
}
}