blob: 990b11cdcd4a738715ebf964b5d8373e8e5b3771 [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.reasoner;
import java.util.Map;
import org.apache.jena.graph.* ;
import org.apache.jena.reasoner.rulesys.ClauseEntry ;
import org.apache.jena.reasoner.rulesys.Functor ;
import org.apache.jena.reasoner.rulesys.Node_RuleVariable ;
import org.apache.jena.util.CollectionFactory ;
import org.apache.jena.vocabulary.RDF ;
import org.apache.jena.vocabulary.RDFS ;
/**
* Datastructure which defines a triple pattern as used in simple
* rules and in find interfaces.
* <p>
* Wildcards are recorded by using Node_RuleVariable entries rather than
* nulls because they can be named. If a null is specified that is
* converted to a variable of name "". Note that whilst some engines might simply
* require Node_Variables the forward engine requires variables represented using
* the more specialized subclass - Node_RuleVariable.</p>
* <p>
* It would make more sense to have TriplePattern subclass Triple
* but that is final for some strange reason.</p>
*/
public class TriplePattern implements ClauseEntry {
/** The subject element of the pattern */
protected Node subject;
/** The predicate element of the pattern */
protected Node predicate;
/** The object element of the pattern */
protected Node object;
/**
* Constructor - builds a pattern from three nodes,
* use Node_RuleVariables as variables, use a variable
* with an empty name as a wildcard, can also use null
* as a wildcard.
*/
public TriplePattern(Node subject, Node predicate, Node object) {
this.subject = normalize(subject);
this.predicate = normalize(predicate);
this.object = normalize(object);
}
/**
* Constructor - builds a degenerate pattern from a simple triple.
* This would be much easier if we merged Triples and TriplePatterns!
*/
public TriplePattern(Triple match) {
this.subject = normalize(match.getSubject());
this.predicate = normalize(match.getPredicate());
this.object = normalize(match.getObject());
}
/**
* Returns the object.
* @return Node
*/
public Node getObject() {
return object;
}
/**
* Returns the predicate.
* @return Node
*/
public Node getPredicate() {
return predicate;
}
/**
* Returns the subject.
* @return Node
*/
public Node getSubject() {
return subject;
}
/**
* Return the triple pattern as a triple match (concrete terms + Node.ANY)
*/
public Triple asTripleMatch() {
return Triple.createMatch(toMatch(subject),
toMatch(predicate),
toMatch(object));
}
/**
* Return the triple pattern as a triple
*/
public Triple asTriple() {
return Triple.create(subject,predicate, object);
}
/**
* Compare two patterns for compatibility - i.e. potentially unifiable.
* Two patterns are "compatible" in the sense we mean here
* if all their ground terms match. A variable in either pattern
* can match a ground term or a variable in the other. We are not,
* currently, checking for multiple occurances of the same variable.
* Functor-valued object literals are treated as a special case which
* are only checked for name/arity matching.
*/
public boolean compatibleWith(TriplePattern pattern) {
boolean ok = subject.isVariable() || pattern.subject.isVariable() || subject.equals(pattern.subject);
if (!ok) return false;
ok = predicate.isVariable() || pattern.predicate.isVariable() || predicate.equals(pattern.predicate);
if (!ok) return false;
if (object.isVariable() || pattern.object.isVariable()) return true;
// Left with checking compatibility of ground literals
if (Functor.isFunctor(object) && Functor.isFunctor(pattern.object)) {
Functor functor = (Functor)object.getLiteralValue();
Functor pFunctor = (Functor)pattern.object.getLiteralValue();
return (functor.getName().equals(pFunctor.getName())
&& functor.getArgs().length == pFunctor.getArgs().length);
} else {
return object.sameValueAs(pattern.object);
}
}
/**
* Test if a pattern is just a variant of this pattern. I.e. it is the same
* up to variable renaming. This takes into account multiple occurances
* of the same variable.
*/
public boolean variantOf(TriplePattern pattern) {
Map<Node, Node> vmap = CollectionFactory.createHashedMap();
if ( ! variantOf(subject, pattern.subject, vmap) ) return false;
if ( ! variantOf(predicate, pattern.predicate, vmap) ) return false;
if (Functor.isFunctor(object) && Functor.isFunctor(pattern.object)) {
Functor functor = (Functor)object.getLiteralValue();
Functor pFunctor = (Functor)pattern.object.getLiteralValue();
if ( ! functor.getName().equals(pFunctor.getName()) ) return false;
Node[] args = functor.getArgs();
Node[] pargs = pFunctor.getArgs();
if ( args.length != pargs.length ) return false;
for (int i = 0; i < args.length; i++) {
if ( ! variantOf(args[i], pargs[i], vmap) ) return false;
}
return true;
} else {
return variantOf(object, pattern.object, vmap);
}
}
/**
* Test if one node is a variant of another give a table of variable matches.
*/
private boolean variantOf(Node n, Node p, Map<Node, Node> vmap) {
if (n instanceof Node_RuleVariable) {
if (p instanceof Node_RuleVariable) {
Object nMatch = vmap.get(n);
if (nMatch == null) {
// First match of these pairs
vmap.put(n, p);
return true;
} else {
return nMatch == p;
}
} else {
return false;
}
} else {
return n.sameValueAs(p);
}
}
/**
* Check a pattern to see if it is legal, used to exclude backchaining goals that
* could never be satisfied. A legal pattern cannot have literals in the subject or
* predicate positions and is not allowed nested functors in the object.
*/
public boolean isLegal() {
if (subject.isLiteral() || predicate.isLiteral()) return false;
if (Functor.isFunctor(subject)) return false;
if (Functor.isFunctor(object)) {
Node[] args = ((Functor)object.getLiteralValue()).getArgs();
for ( Node arg : args )
{
if ( Functor.isFunctor( arg ) )
{
return false;
}
}
}
return true;
}
/**
* Compare two patterns and return true if arg is a more
* specific (more grounded) version of this one.
* Does not handle functors.
*/
public boolean subsumes(TriplePattern arg) {
return (subject.isVariable() || subject.equals(arg.subject))
&& (predicate.isVariable() || predicate.equals(arg.predicate))
&& (object.isVariable() || object.equals(arg.object));
}
/**
* Test if the pattern is ground, contains no variables.
*/
public boolean isGround() {
if (subject.isVariable() || predicate.isVariable() || object.isVariable()) return false;
if (Functor.isFunctor(object)) {
return ((Functor)object.getLiteralValue()).isGround();
}
return true;
}
/**
* Printable string
*/
@Override
public String toString() {
return simplePrintString(subject) +
" @" + simplePrintString(predicate) +
" " + simplePrintString(object);
}
/**
* Simplified printable name for a triple
*/
public static String simplePrintString(Triple t) {
return simplePrintString(t.getSubject()) +
" @" + simplePrintString(t.getPredicate()) +
" " + simplePrintString(t.getObject());
}
/**
* Simplified printable name for a node
*/
public static String simplePrintString(Node n) {
if (n instanceof Node_URI) {
String uri = n.getURI();
int split = uri.lastIndexOf('#');
if (split == -1) {
split = uri.lastIndexOf('/');
if (split == -1) split = -1;
}
String ns = uri.substring(0, split+1);
String prefix = "";
if (ns.equals(RDF.getURI())) {
prefix = "rdf:";
} else if (ns.equals(RDFS.getURI())) {
prefix = "rdfs:";
}
return prefix + uri.substring(split+1);
} else {
return n.toString();
}
}
/**
* Convert any null wildcards to Node_RuleVariable wildcards.
*/
private static Node normalize(Node node) {
if (node == null || node == Node.ANY)
return Node_RuleVariable.WILD;
return node;
}
/**
* Convert any Node_RuleVariable wildcards to null. This loses
* the variable named but is used when converting a singleton
* pattern to a TripleMtch
*/
private static Node toMatch(Node node) {
return node.isVariable() ? null : node;
}
/**
* Equality override - used so that TriplePattern variants (same to within variable renaming) test as equals
*/
@Override
public boolean equals(Object o) {
// return o instanceof TriplePattern &&
// subject.equals(((TriplePattern)o).subject) &&
// predicate.equals(((TriplePattern)o).predicate) &&
// object.equals(((TriplePattern)o).object);
return o instanceof TriplePattern &&
nodeEqual(subject, ((TriplePattern)o).subject) &&
nodeEqual(predicate, ((TriplePattern)o).predicate) &&
nodeEqual(object, ((TriplePattern)o).object);
}
/** Helper - equality override on nodes */
private boolean nodeEqual(Node n1, Node n2) {
if ((n1 instanceof Node_RuleVariable) && (n2 instanceof Node_RuleVariable)) {
return true;
} else {
return n1.equals(n2);
}
}
/** hash function override */
@Override
public int hashCode() {
int hash = 0;
if (!(subject instanceof Node_RuleVariable)) hash ^= (subject.hashCode() >> 1);
if (!(predicate instanceof Node_RuleVariable)) hash ^= predicate.hashCode();
if (!(object instanceof Node_RuleVariable)) hash ^= (object.hashCode() << 1);
return hash;
// return (subject.hashCode() >> 1) ^ predicate.hashCode() ^ (object.hashCode() << 1);
}
/**
* Compare triple patterns, taking into account variable indices.
* The equality function ignores differences between variables.
*/
@Override
public boolean sameAs(Object o) {
if (! (o instanceof TriplePattern) ) return false;
TriplePattern other = (TriplePattern) o;
return Node_RuleVariable.sameNodeAs(subject, other.subject) && Node_RuleVariable.sameNodeAs(predicate, other.predicate) && Node_RuleVariable.sameNodeAs(object, other.object);
}
}