/* * 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.rulesys; import org.apache.jena.datatypes.* ; import org.apache.jena.graph.* ; import org.apache.jena.util.PrintUtil ; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.function.Predicate; /** * A functor comprises a functor name and a list of * arguments. The arguments are Nodes of any type except functor nodes * (there is no functor nesting). Functors play three roles in rules - * in heads they represent actions (procedural attachement); in bodies they * represent builtin predicates; in TriplePatterns they represent embedded * structured literals that are used to cache matched subgraphs such as * restriction specifications. */ public class Functor implements ClauseEntry { /** Functor's name */ protected String name; /** Argument list - an array of nodes */ protected Node[] args; /** A built in that implements the functor */ protected Builtin implementor; /** A static Predicate instance that detects triples with Functor objects */ public static final Predicate<Triple> acceptFilter = t -> { if (t.getSubject().isLiteral()) return true; Node n = t.getObject(); return n.isLiteral() && n.getLiteralDatatype() == FunctorDatatype.theFunctorDatatype; }; protected static Logger logger = LoggerFactory.getLogger(Functor.class); /** * Constructor. * @param name the name of the functor * @param args an array of nodes defining the arguments, this will not be copied so beware of * accidental structure sharing */ public Functor(String name, Node[] args) { this.name = name; this.args = args; } /** * Constructor * @param name the name of the functor * @param args a list of nodes defining the arguments * @param registry a table of builtins to consult to check for * implementations of this functor when used as a rule clause */ public Functor(String name, List<Node> args, BuiltinRegistry registry) { this(name, args,registry.getImplementation(name)); } /** * Constructor * @param name the name of the functor * @param args an array of nodes defining the arguments * @param registry a table of builtins to consult to check for * implementations of this functor when used as a rule clause */ public Functor(String name,Node[] args, BuiltinRegistry registry) { this(name, args,registry.getImplementation(name)); } /** * Constructor * @param name the name of the functor * @param args a list of nodes defining the arguments * @param impl a specific builtin implementation of this functor * */ public Functor(String name, List<Node> args, Builtin impl) { this.name = name; this.args = args.toArray(new Node[]{}); this.implementor = impl; } /** * Constructor * @param name the name of the functor * @param args a list of nodes defining the arguments * @param impl a specific builtin implementation of this functor * */ public Functor(String name, Node[] args, Builtin impl) { this.name = name; this.args = args; this.implementor = impl; } /** * Return the functor name */ public String getName() { return name; } /** * Return the functor aguments as an array of nodes */ public Node[] getArgs() { return args; } /** * Return the length of the functor argument array. */ public int getArgLength() { return args.length; } /** * Returns true if the functor is fully ground, no variables */ public boolean isGround() { for ( Node n : args ) { if ( n instanceof Node_RuleVariable || n instanceof Node_ANY ) { return false; } } return true; } /** * Returns true if the functor is fully ground in the given environment */ public boolean isGround(BindingEnvironment env) { for ( Node n : args ) { if ( env.getGroundVersion( n ).isVariable() ) { return false; } } return true; } /** * Execute the given built in as a body clause. * @param context an execution context giving access to other relevant data * @return true if the functor has an implementation and that implementation returns true when evaluated */ public boolean evalAsBodyClause(RuleContext context) { if (getImplementor() == null) { logger.warn("Invoking undefined functor " + getName() + " in " + context.getRule().toShortString()); return false; } return implementor.bodyCall(getBoundArgs(context.getEnv()), args.length, context); } /** * Execute the given built in as a body clause, only if it is side-effect-free. * @param context an execution context giving access to other relevant data * @return true if the functor has an implementation and that implementation returns true when evaluated */ public boolean safeEvalAsBodyClause(RuleContext context) { if (getImplementor() == null) { logger.warn("Invoking undefined functor " + getName() + " in " + context.getRule().toShortString()); return false; } if (implementor.isSafe()) { return implementor.bodyCall(getBoundArgs(context.getEnv()), args.length, context); } else { return false; } } /** * Return a new Node array containing the bound versions of this Functor's arguments */ public Node[] getBoundArgs(BindingEnvironment env) { Node[] boundargs = new Node[args.length]; for (int i = 0; i < args.length; i++) { boundargs[i] = env.getGroundVersion(args[i]); } return boundargs; } /** * Return the Builtin that implements this functor * @return the Builtin or null if there isn't one */ public Builtin getImplementor() { if (implementor == null) { implementor = BuiltinRegistry.theRegistry.getImplementation(name); } return implementor; } /** * Set the Builtin that implements this functor. */ public void setImplementor(Builtin implementor) { this.implementor = implementor; } /** * Printable string describing the functor */ @Override public String toString() { StringBuilder buff = new StringBuilder(name); buff.append("("); for (int i = 0; i < args.length; i++) { buff.append(PrintUtil.print(args[i])); if (i < args.length - 1) { buff.append(" "); } } buff.append(")"); return buff.toString(); } /** * tests that a given Node represents a functor */ public static boolean isFunctor(Node n) { if (n == null) return false; return n.isLiteral() && n.getLiteralDatatype() == FunctorDatatype.theFunctorDatatype; } /** * Equality is based on structural comparison */ @Override public boolean equals(Object obj) { if (obj instanceof Functor) { Functor f2 = (Functor)obj; if (name.equals(f2.name) && args.length == f2.args.length) { for (int i = 0; i < args.length; i++) { if (!args[i].sameValueAs(f2.args[i])) return false; } return true; } } return false; } /** hash function override */ @Override public int hashCode() { return (name.hashCode()) ^ (args.length << 2); } /** * Compare Functors, taking into account variable indices. * The equality function ignores differences between variables. */ @Override public boolean sameAs(Object o) { if (o instanceof Functor) { Functor f2 = (Functor)o; if (name.equals(f2.name) && args.length == f2.args.length) { for (int i = 0; i < args.length; i++) { if (! Node_RuleVariable.sameNodeAs(args[i], f2.args[i])) return false; } return true; } } return false; } /** * Create a functor and wrap it up as a Literal node * @param name the name of the functor * @param args an array of nodes defining the arguments, this will not be copied so beware of * accidental structure sharing */ public static Node makeFunctorNode(String name, Node[] args) { return makeFunctorNode( new Functor( name, args, BuiltinRegistry.theRegistry ) ); } /** * Wrap a functor as a Literal node * @param f the functor data structure to be wrapped in a node. */ public static Node makeFunctorNode(Functor f) { return NodeFactory.createLiteralByValue(f, FunctorDatatype.theFunctorDatatype); } /** * Inner class. Dummy datatype definition for * functor-valued literals. */ public static class FunctorDatatype extends BaseDatatype { public FunctorDatatype() { super("urn:x-hp-jena:Functor"); } public static final RDFDatatype theFunctorDatatype = new FunctorDatatype(); } }