001: /******************************************************************
002: * File: FBRuleReasoner.java
003: * Created by: Dave Reynolds
004: * Created on: 29-May-2003
005: *
006: * (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
007: * [See end of file]
008: * $Id: FBRuleReasoner.java,v 1.26 2008/01/02 12:07:47 andy_seaborne Exp $
009: *****************************************************************/package com.hp.hpl.jena.reasoner.rulesys;
010:
011: import com.hp.hpl.jena.rdf.model.*;
012: import com.hp.hpl.jena.reasoner.*;
013: import com.hp.hpl.jena.shared.*;
014: import com.hp.hpl.jena.vocabulary.ReasonerVocabulary;
015: import com.hp.hpl.jena.graph.*;
016:
017: import java.util.*;
018:
019: /**
020: * Rule-based reasoner interface. This is the default rule reasoner to use.
021: * It supports both forward reasoning and backward reasoning, including use
022: * of forward rules to generate and instantiate backward rules.
023: *
024: * @author <a href="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>
025: * @version $Revision: 1.26 $ on $Date: 2008/01/02 12:07:47 $
026: */
027: public class FBRuleReasoner implements RuleReasoner {
028:
029: /** The parent reasoner factory which is consulted to answer capability questions */
030: protected ReasonerFactory factory;
031:
032: /** The rules to be used by this instance of the forward engine */
033: protected List rules = new ArrayList();
034:
035: /** A precomputed set of schema deductions */
036: protected Graph schemaGraph;
037:
038: /** Flag to set whether the inference class should record derivations */
039: protected boolean recordDerivations = false;
040:
041: /** Flag which, if true, enables tracing of rule actions to logger.info */
042: boolean traceOn = false;
043: // boolean traceOn = true;
044:
045: /** Flag, if true we cache the closure of the pure rule set with its axioms */
046: protected static final boolean cachePreload = true;
047:
048: /** The cached empty closure, if wanted */
049: protected InfGraph preload;
050:
051: /** The original configuration properties, if any */
052: protected Resource configuration;
053:
054: /** The graph capabilities of the infgraphs generated by this reasoner */
055: protected Capabilities capabilities;
056:
057: /**
058: * Constructor. This is the raw version that does not reference a ReasonerFactory
059: * and so has no capabilities description.
060: * @param rules a list of Rule instances which defines the ruleset to process
061: */
062: public FBRuleReasoner(List rules) {
063: if (rules == null)
064: throw new NullPointerException("null rules");
065: this .rules = rules;
066: }
067:
068: /**
069: * Constructor
070: * @param factory the parent reasoner factory which is consulted to answer capability questions
071: */
072: public FBRuleReasoner(ReasonerFactory factory) {
073: this (new ArrayList(), factory);
074: }
075:
076: /**
077: * Constructor
078: * @param factory the parent reasoner factory which is consulted to answer capability questions
079: * @param configuration RDF node to configure the rule set and mode, can be null
080: */
081: public FBRuleReasoner(ReasonerFactory factory,
082: Resource configuration) {
083: this (new ArrayList(), factory);
084: this .configuration = configuration;
085: if (configuration != null)
086: loadConfiguration(configuration);
087: }
088:
089: /**
090: load the configuration from the configuring Resource (in its Model).
091: */
092: protected void loadConfiguration(Resource configuration) {
093: StmtIterator i = configuration.listProperties();
094: while (i.hasNext()) {
095: Statement st = i.nextStatement();
096: doSetRDFNodeParameter(st.getPredicate(), st.getObject());
097: }
098: }
099:
100: /**
101: * Constructor
102: * @param rules a list of Rule instances which defines the ruleset to process
103: * @param factory the parent reasoner factory which is consulted to answer capability questions
104: */
105: public FBRuleReasoner(List rules, ReasonerFactory factory) {
106: this (rules);
107: this .factory = factory;
108: }
109:
110: /**
111: * Internal constructor, used to generated a partial binding of a schema
112: * to a rule reasoner instance.
113: */
114: protected FBRuleReasoner(List rules, Graph schemaGraph,
115: ReasonerFactory factory) {
116: this (rules, factory);
117: this .schemaGraph = schemaGraph;
118: }
119:
120: /**
121: Add the given rules to the current set and answer this Reasoner. Provided
122: so that the Factory can deal out reasoners with specified rulesets.
123: There may well be a better way to arrange this.
124: TODO review & revise
125: */
126: public FBRuleReasoner addRules(List rules) {
127: List combined = new ArrayList(this .rules);
128: combined.addAll(rules);
129: setRules(combined);
130: return this ;
131: }
132:
133: /**
134: * Return a description of the capabilities of this reasoner encoded in
135: * RDF. These capabilities may be static or may depend on configuration
136: * information supplied at construction time. May be null if there are
137: * no useful capabilities registered.
138: */
139: public Model getReasonerCapabilities() {
140: if (factory != null) {
141: return factory.getCapabilities();
142: } else {
143: return null;
144: }
145: }
146:
147: /**
148: * Add a configuration description for this reasoner into a partial
149: * configuration specification model.
150: * @param configSpec a Model into which the configuration information should be placed
151: * @param base the Resource to which the configuration parameters should be added.
152: */
153: public void addDescription(Model configSpec, Resource base) {
154: if (configuration != null) {
155: StmtIterator i = configuration.listProperties();
156: while (i.hasNext()) {
157: Statement st = i.nextStatement();
158: configSpec.add(base, st.getPredicate(), st.getObject());
159: }
160: }
161: }
162:
163: /**
164: * Determine whether the given property is recognized and treated specially
165: * by this reasoner. This is a convenience packaging of a special case of getCapabilities.
166: * @param property the property which we want to ask the reasoner about, given as a Node since
167: * this is part of the SPI rather than API
168: * @return true if the given property is handled specially by the reasoner.
169: */
170: public boolean supportsProperty(Property property) {
171: if (factory == null)
172: return false;
173: Model caps = factory.getCapabilities();
174: Resource root = caps.getResource(factory.getURI());
175: return caps.contains(root, ReasonerVocabulary.supportsP,
176: property);
177: }
178:
179: /**
180: * Precompute the implications of a schema graph. The statements in the graph
181: * will be combined with the data when the final InfGraph is created.
182: */
183: public Reasoner bindSchema(Graph tbox) throws ReasonerException {
184: if (schemaGraph != null) {
185: throw new ReasonerException(
186: "Can only bind one schema at a time to an OWLRuleReasoner");
187: }
188: FBRuleInfGraph graph = new FBRuleInfGraph(this , rules,
189: getPreload(), tbox);
190: graph.prepare();
191: FBRuleReasoner fbr = new FBRuleReasoner(rules, graph, factory);
192: fbr.setDerivationLogging(recordDerivations);
193: fbr.setTraceOn(traceOn);
194: return fbr;
195: }
196:
197: /**
198: * Precompute the implications of a schema Model. The statements in the graph
199: * will be combined with the data when the final InfGraph is created.
200: */
201: public Reasoner bindSchema(Model tbox) throws ReasonerException {
202: return bindSchema(tbox.getGraph());
203: }
204:
205: /**
206: * Attach the reasoner to a set of RDF data to process.
207: * The reasoner may already have been bound to specific rules or ontology
208: * axioms (encoded in RDF) through earlier bindRuleset calls.
209: *
210: * @param data the RDF data to be processed, some reasoners may restrict
211: * the range of RDF which is legal here (e.g. syntactic restrictions in OWL).
212: * @return an inference graph through which the data+reasoner can be queried.
213: * @throws ReasonerException if the data is ill-formed according to the
214: * constraints imposed by this reasoner.
215: */
216: public InfGraph bind(Graph data) throws ReasonerException {
217: ReificationStyle style = data.getReifier().getStyle();
218: Graph schemaArg = schemaGraph == null ? getPreload()
219: : (FBRuleInfGraph) schemaGraph;
220: FBRuleInfGraph graph = new FBRuleInfGraph(this , rules,
221: schemaArg, style);
222: graph.setDerivationLogging(recordDerivations);
223: graph.setTraceOn(traceOn);
224: graph.rebind(data);
225: return graph;
226: }
227:
228: /**
229: * Set (or change) the rule set that this reasoner should execute.
230: * @param rules a list of Rule objects
231: */
232: public void setRules(List rules) {
233: this .rules = rules;
234: preload = null;
235: if (schemaGraph != null) {
236: // The change of rules invalidates the existing precomputed schema graph
237: // This might be recoverable but for now simply flag the error and let the
238: // user reorder their code to set the rules before doing a bind!
239: throw new ReasonerException(
240: "Cannot change the rule set for a bound rule reasoner.\nSet the rules before calling bindSchema");
241: }
242: }
243:
244: /**
245: * Return the list of Rules used by this reasoner
246: * @return a List of Rule objects
247: */
248: public List getRules() {
249: return rules;
250: }
251:
252: /**
253: Answer the list of rules loaded from the given filename. May throw a
254: ReasonerException wrapping an IOException.
255: */
256: public static List loadRules(String fileName) {
257: try {
258: return Rule.parseRules(Util
259: .loadRuleParserFromResourceFile(fileName));
260: } catch (WrappedIOException e) {
261: throw new ReasonerException("Can't load rules file: "
262: + fileName, e.getCause());
263: }
264: }
265:
266: /**
267: * Register an RDF predicate as one whose presence in a goal should force
268: * the goal to be tabled. This is better done directly in the rule set.
269: */
270: public synchronized void tablePredicate(Node predicate) {
271: // Create a dummy rule which tables the predicate ...
272: Rule tablePredicateRule = new Rule("",
273: new ClauseEntry[] { new Functor("table",
274: new Node[] { predicate }) },
275: new ClauseEntry[] {});
276: // ... end append the rule to the ruleset
277: rules.add(tablePredicateRule);
278: }
279:
280: /**
281: * Get the single static precomputed rule closure.
282: */
283: protected synchronized InfGraph getPreload() {
284: if (cachePreload && preload == null) {
285: preload = (new FBRuleInfGraph(this , rules, null));
286: preload.prepare();
287: }
288: return preload;
289: }
290:
291: /**
292: * Switch on/off drivation logging.
293: * If set to true then the InfGraph created from the bind operation will start
294: * life with recording of derivations switched on. This is currently only of relevance
295: * to rule-based reasoners.
296: * <p>
297: * Default - false.
298: */
299: public void setDerivationLogging(boolean logOn) {
300: recordDerivations = logOn;
301: }
302:
303: /**
304: * Set the state of the trace flag. If set to true then rule firings
305: * are logged out to the Log at "INFO" level.
306: */
307: public void setTraceOn(boolean state) {
308: traceOn = state;
309: }
310:
311: /**
312: * Return the state of the trace flag.If set to true then rule firings
313: * are logged out to the Log at "INFO" level.
314: */
315: public boolean isTraceOn() {
316: return traceOn;
317: }
318:
319: /**
320: * Set a configuration parameter for the reasoner. The supported parameters
321: * are:
322: * <ul>
323: * <li>PROPderivationLogging - set to true to enable recording all rule derivations</li>
324: * <li>PROPtraceOn - set to true to enable verbose trace information to be sent to the logger INFO channel</li>
325: * </ul>
326: *
327: * @param parameter the property identifying the parameter to be changed
328: * @param value the new value for the parameter, typically this is a wrapped
329: * java object like Boolean or Integer.
330: * @throws IllegalParameterException if the parameter is unknown
331: */
332: public void setParameter(Property parameter, Object value) {
333: if (!doSetParameter(parameter, value)) {
334: throw new IllegalParameterException(
335: "RuleReasoner does not recognize configuration parameter "
336: + parameter);
337: } else {
338: // Record the configuration change
339: if (configuration == null) {
340: Model configModel = ModelFactory.createDefaultModel();
341: configuration = configModel.createResource();
342: }
343: Util.updateParameter(configuration, parameter, value);
344: }
345: }
346:
347: /**
348: Set a parameter from a statement, given the property and its RDFNode value.
349: Most parameters are, historically, set from the string value of the RDFNode,
350: but newer parameters may have Resource values with embedded models,
351: for which their toString() is not just suspect, but definitively wrong. Hence the
352: introduction of this relay station.
353:
354: @param parameter the propoerty naming the value to set
355: @param value the RDFNode with the value of that property
356: @return true if the property was understood, false otherwise
357: */
358: protected boolean doSetRDFNodeParameter(Property parameter,
359: RDFNode value) {
360: return (value instanceof Resource && doSetResourceParameter(
361: parameter, (Resource) value))
362: || (value instanceof Literal && doSetParameter(
363: parameter, ((Literal) value).getValue()))
364: || doSetParameter(parameter, value.toString());
365: }
366:
367: /**
368: Set a parameter with a Resource value. Answer false if the parameter is not
369: understood. Default understands no parameters; subclasses may override.
370: */
371: protected boolean doSetResourceParameter(Property parameter,
372: Resource value) {
373: return false;
374: }
375:
376: /**
377: * Set a configuration parameter for the reasoner. The supported parameters
378: * are:
379: * <ul>
380: * <li>PROPderivationLogging - set to true to enable recording all rule derivations</li>
381: * <li>PROPtraceOn - set to true to enable verbose trace information to be sent to the logger INFO channel</li>
382: * </ul>
383: * @param parameter the property identifying the parameter to be changed
384: * @param value the new value for the parameter, typically this is a wrapped
385: * java object like Boolean or Integer.
386: * @return false if the parameter was not known
387: */
388: protected boolean doSetParameter(Property parameter, Object value) {
389: if (parameter.equals(ReasonerVocabulary.PROPderivationLogging)) {
390: recordDerivations = Util.convertBooleanPredicateArg(
391: parameter, value);
392: return true;
393: } else if (parameter.equals(ReasonerVocabulary.PROPtraceOn)) {
394: traceOn = Util.convertBooleanPredicateArg(parameter, value);
395: return true;
396: } else {
397: return false;
398: }
399: }
400:
401: /**
402: * Return the Jena Graph Capabilties that the inference graphs generated
403: * by this reasoner are expected to conform to.
404: */
405: public Capabilities getGraphCapabilities() {
406: if (capabilities == null) {
407: capabilities = new BaseInfGraph.InfCapabilities();
408: }
409: return capabilities;
410: }
411:
412: }
413:
414: /*
415: (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
416: All rights reserved.
417:
418: Redistribution and use in source and binary forms, with or without
419: modification, are permitted provided that the following conditions
420: are met:
421:
422: 1. Redistributions of source code must retain the above copyright
423: notice, this list of conditions and the following disclaimer.
424:
425: 2. Redistributions in binary form must reproduce the above copyright
426: notice, this list of conditions and the following disclaimer in the
427: documentation and/or other materials provided with the distribution.
428:
429: 3. The name of the author may not be used to endorse or promote products
430: derived from this software without specific prior written permission.
431:
432: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
433: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
434: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
435: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
436: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
437: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
438: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
439: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
440: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
441: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
442: */
|