KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > hp > hpl > jena > reasoner > rulesys > Rule


1 /******************************************************************
2  * File: Rule.java
3  * Created by: Dave Reynolds
4  * Created on: 29-Mar-03
5  *
6  * (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
7  * [See end of file]
8  * $Id: Rule.java,v 1.30 2005/04/10 14:19:32 der Exp $
9  *****************************************************************/

10 package com.hp.hpl.jena.reasoner.rulesys;
11
12 import java.io.BufferedReader JavaDoc;
13 import java.io.IOException JavaDoc;
14 import java.util.*;
15
16 import com.hp.hpl.jena.util.FileUtils;
17 import com.hp.hpl.jena.util.PrintUtil;
18 import com.hp.hpl.jena.util.Tokenizer;
19 import com.hp.hpl.jena.graph.*;
20 import com.hp.hpl.jena.reasoner.*;
21 import com.hp.hpl.jena.shared.*;
22 import com.hp.hpl.jena.datatypes.xsd.*;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 /** * Representation of a generic inference rule.
28  * <p>
29  * This represents the rule specification but most engines will
30  * compile this specification into an abstract machine or processing
31  * graph. </p>
32  * <p>
33  * The rule specification comprises a list of antecendents (body) and a list
34  * of consequents (head). If there is more than one consequent then a backchainer
35  * should regard this as a shorthand for several rules, all with the
36  * same body but with a singleton head. </p>
37  * <p>
38  * Each element in the head or body can be a TriplePattern, a Functor or a Rule.
39  * A TriplePattern is just a triple of Nodes but the Nodes can represent
40  * variables, wildcards and embedded functors - as well as constant uri or
41  * literal graph nodes. A functor comprises a functor name and a list of
42  * arguments. The arguments are Nodes of any type except functor nodes
43  * (there is no functor nesting). The functor name can be mapped into a registered
44  * java class that implements its semantics. Functors play three roles -
45  * in heads they represent actions (procedural attachement); in bodies they
46  * represent builtin predicates; in TriplePatterns they represent embedded
47  * structured literals that are used to cache matched subgraphs such as
48  * restriction specifications. </p>
49  *<p>
50  * We include a trivial, recursive descent parser but this is just there
51  * to allow rules to be embedded in code. External rule syntax based on N3
52  * and RDF could be developed. The embedded syntax supports rules such as:
53  * <blockindent>
54  * <code>[ (?C rdf:type *), guard(?C, ?P) -> (?c rb:restriction some(?P, ?D)) ].</code><br />
55  * <code>[ (?s owl:foo ?p) -> [ (?s owl:bar ?a) -> (?s ?p ?a) ] ].</code><br />
56  * <code>[name: (?s owl:foo ?p) -> (?s ?p ?a)].</code><br />
57  * </blockindent>
58  * only built in namespaces are recognized as such, * is a wildcard node, ?c is a variable,
59  * name(node ... node) is a functor, (node node node) is a triple pattern, [..] is an
60  * embedded rule, commas are ignore and can be freely used as separators. Functor names
61  * may not end in ':'.
62  * </p>
63  * * @author <a HREF="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a> * @version $Revision: 1.30 $ on $Date: 2005/04/10 14:19:32 $ */

64 public class Rule implements ClauseEntry {
65     
66 //=======================================================================
67
// variables
68

69     /** Rule body */
70     protected ClauseEntry[] body;
71     
72     /** Rule head or set of heads */
73     protected ClauseEntry[] head;
74     
75     /** Optional name for the rule */
76     protected String JavaDoc name;
77     
78     /** The number of distinct variables used in the rule */
79     protected int numVars = -1;
80     
81     /** Flags whether the rule was written as a forward or backward rule */
82     protected boolean isBackward = false;
83     
84     static Log logger = LogFactory.getLog(Rule.class);
85     
86     /**
87      * Constructor
88      * @param body a list of TriplePatterns or Functors.
89      * @param head a list of TriplePatterns, Functors or rules
90      */

91     public Rule(List head, List body) {
92         this(null, head, body);
93     }
94     
95     /**
96      * Constructor
97      * @param name a label for rule
98      * @param body a list of TriplePatterns or Functors.
99      * @param head a list of TriplePatterns, Functors or rules
100      */

101     public Rule(String JavaDoc name, List head, List body) {
102         this.name = name;
103         this.head = (ClauseEntry[]) head.toArray(new ClauseEntry[head.size()]);
104         this.body = (ClauseEntry[]) body.toArray(new ClauseEntry[body.size()]);
105     }
106     
107     /**
108      * Constructor
109      * @param name a label for rule
110      * @param body an array of TriplePatterns or Functors.
111      * @param head an array of TriplePatterns, Functors or rules
112      */

113     public Rule(String JavaDoc name, ClauseEntry[] head, ClauseEntry[] body) {
114         this.name = name;
115         this.head = head;
116         this.body = body;
117     }
118     
119 //=======================================================================
120
// accessors
121

122     /**
123      * Return the number of body elements
124      */

125     public int bodyLength() {
126         return body.length;
127     }
128     
129     /**
130      * Return the n'th body element
131      */

132     public ClauseEntry getBodyElement(int n) {
133         return body[n];
134     }
135     
136     /**
137      * return the entire rule body as an array of objects
138      */

139     public ClauseEntry[] getBody() {
140         return body;
141     }
142         
143     
144     /**
145      * Return the number of head elements
146      */

147     public int headLength() {
148         return head.length;
149     }
150     
151     /**
152      * Return the n'th head element
153      */

154     public ClauseEntry getHeadElement(int n) {
155         return head[n];
156     }
157     
158     /**
159      * return the entire rule head as an array of objects
160      */

161     public ClauseEntry[] getHead() {
162         return head;
163     }
164     
165     /**
166      * Return true if the rule was written as a backward (as opposed to forward) rule.
167      */

168     public boolean isBackward() {
169         return isBackward;
170     }
171     
172     /**
173      * Set the rule to be run backwards.
174      * @param flag if true the rule should run backwards.
175      */

176     public void setBackward(boolean flag) {
177         isBackward = flag;
178     }
179     
180     /**
181      * Get the name for the rule - can be null.
182      */

183     public String JavaDoc getName() {
184         return name;
185     }
186     
187     /**
188      * Set the number of distinct variables for this rule.
189      * Used internally when cloing rules, not normally required.
190      */

191     public void setNumVars(int n) {
192         numVars = n;
193     }
194     
195     /**
196      * Return the number of distinct variables in the rule. Or more precisely, the
197      * size of a binding environment needed to represent the rule.
198      */

199     public int getNumVars() {
200         if (numVars == -1) {
201             // only have to do this if the rule was generated programatically
202
// the parser will have prefilled this in for normal rules
203
int max = findVars(body, -1);
204             max = findVars(head, max);
205             numVars = max + 1;
206         }
207         return numVars;
208     }
209     
210     /**
211      * Find all the variables in a clause array.
212      */

213     private int findVars(Object JavaDoc[] nodes, int maxIn) {
214         int max = maxIn;
215         for (int i = 0; i < nodes.length; i++) {
216             Object JavaDoc node = nodes[i];
217             if (node instanceof TriplePattern) {
218                 max = findVars((TriplePattern)node, max);
219             } else {
220                 max = findVars((Functor)node, max);
221             }
222         }
223         return max;
224     }
225     
226     /**
227      * Find all the variables in a TriplePattern.
228      */

229     private int findVars(TriplePattern t, int maxIn) {
230         int max = maxIn;
231         max = maxVarIndex(t.getSubject(), max);
232         max = maxVarIndex(t.getPredicate(), max);
233         Node obj = t.getObject();
234         if (obj instanceof Node_RuleVariable) {
235             max = maxVarIndex(obj, max);
236         } else if (Functor.isFunctor(obj)) {
237             max = findVars((Functor)obj.getLiteral().getValue(), max);
238         }
239         return max;
240     }
241         
242     /**
243      * Find all the variables in a Functor.
244      */

245     private int findVars(Functor f, int maxIn) {
246         int max = maxIn;
247         Node[] args = f.getArgs();
248         for (int i = 0; i < args.length; i++) {
249             if (args[i].isVariable()) max = maxVarIndex(args[i], max);
250         }
251         return max;
252     }
253     
254     /**
255      * Return the maximum node index of the variable and the max so far.
256      */

257     private int maxVarIndex(Node var, int max) {
258         if (var instanceof Node_RuleVariable) {
259             int index = ((Node_RuleVariable)var).index;
260             if (index > max) return index;
261         }
262         return max;
263     }
264     
265     /**
266      * Instantiate a rule given a variable binding environment.
267      * This will clone any non-bound variables though that is only needed
268      * for trail implementations.
269      */

270     public Rule instantiate(BindingEnvironment env) {
271         HashMap vmap = new HashMap();
272         return new Rule(name, cloneClauseArray(head, vmap, env), cloneClauseArray(body, vmap, env));
273     }
274     
275     /**
276      * Clone a rule, cloning any embedded variables.
277      */

278     public Rule cloneRule() {
279         if (getNumVars() > 0) {
280             HashMap vmap = new HashMap();
281             return new Rule(name, cloneClauseArray(head, vmap, null), cloneClauseArray(body, vmap, null));
282         } else {
283             return this;
284         }
285     }
286     
287     /**
288      * Clone a clause array.
289      */

290     private ClauseEntry[] cloneClauseArray(ClauseEntry[] clauses, Map vmap, BindingEnvironment env) {
291         ClauseEntry[] cClauses = new ClauseEntry[clauses.length];
292         for (int i = 0; i < clauses.length; i++ ) {
293             cClauses[i] = cloneClause(clauses[i], vmap, env);
294         }
295         return cClauses;
296     }
297     
298     /**
299      * Clone a clause, cloning any embedded variables.
300      */

301     private ClauseEntry cloneClause(ClauseEntry clause, Map vmap, BindingEnvironment env) {
302         if (clause instanceof TriplePattern) {
303             TriplePattern tp = (TriplePattern)clause;
304             return new TriplePattern (
305                             cloneNode(tp.getSubject(), vmap, env),
306                             cloneNode(tp.getPredicate(), vmap, env),
307                             cloneNode(tp.getObject(), vmap, env)
308                         );
309         } else {
310             return cloneFunctor((Functor)clause, vmap, env);
311         }
312     }
313     
314     /**
315      * Clone a functor, cloning any embedded variables.
316      */

317     private Functor cloneFunctor(Functor f, Map vmap, BindingEnvironment env) {
318         Node[] args = f.getArgs();
319         Node[] cargs = new Node[args.length];
320         for (int i = 0; i < args.length; i++) {
321             cargs[i] = cloneNode(args[i], vmap, env);
322         }
323         Functor fn = new Functor(f.getName(), cargs);
324         fn.setImplementor(f.getImplementor());
325         return fn;
326     }
327     
328     /**
329      * Close a single node.
330      */

331     private Node cloneNode(Node nIn, Map vmap, BindingEnvironment env) {
332         Node n = (env == null) ? nIn : env.getGroundVersion(nIn);
333         if (n instanceof Node_RuleVariable) {
334             Node_RuleVariable nv = (Node_RuleVariable)n;
335             Node c = (Node)vmap.get(nv);
336             if (c == null) {
337                 c = ((Node_RuleVariable)n).cloneNode();
338                 vmap.put(nv, c);
339             }
340             return c;
341         } else if (Functor.isFunctor(n)) {
342             Functor f = (Functor)n.getLiteral().getValue();
343             return Functor.makeFunctorNode(cloneFunctor(f, vmap, env));
344         } else {
345             return n;
346         }
347     }
348     
349     /**
350      * Printable string describing the rule
351      */

352     public String JavaDoc toString() {
353         StringBuffer JavaDoc buff = new StringBuffer JavaDoc();
354         buff.append("[ ");
355         if (name != null) {
356             buff.append(name);
357             buff.append(": ");
358         }
359         if (isBackward) {
360             for (int i = 0; i < head.length; i++) {
361                 buff.append(PrintUtil.print(head[i]));
362                 buff.append(" ");
363             }
364             buff.append("<- ");
365             for (int i = 0; i < body.length; i++) {
366                 buff.append(PrintUtil.print(body[i]));
367                 buff.append(" ");
368             }
369         } else {
370             for (int i = 0; i < body.length; i++) {
371                 buff.append(PrintUtil.print(body[i]));
372                 buff.append(" ");
373             }
374             buff.append("-> ");
375             for (int i = 0; i < head.length; i++) {
376                 buff.append(PrintUtil.print(head[i]));
377                 buff.append(" ");
378             }
379         }
380         buff.append("]");
381         return buff.toString();
382     }
383     
384     /**
385      * Print a short description of the rule, just its name if it
386      * has one, otherwise the whole rule description.
387      */

388     public String JavaDoc toShortString() {
389         if (name != null) {
390             return name;
391         } else {
392             return toString();
393         }
394     }
395     
396 //=======================================================================
397
// parser access
398

399     /**
400      * Parse a string as a rule.
401      * @throws ParserException if there is a problem
402      */

403     public static Rule parseRule(String JavaDoc source) throws ParserException {
404         Parser parser = new Parser(source);
405         return parser.parseRule();
406     }
407     
408     /**
409      * Answer the list of rules parsed from the given URL.
410      * @throws RulesetNotFoundException
411      */

412     public static List rulesFromURL( String JavaDoc uri ) {
413         try {
414             BufferedReader JavaDoc br = FileUtils.readerFromURL( uri );
415             return parseRules( Rule.rulesParserFromReader( br ) );
416         }
417         catch (WrappedIOException e)
418             { throw new RulesetNotFoundException( uri ); }
419     }
420     
421     /**
422     Answer a String which is the concatenation (with newline glue) of all the
423     non-comment lines readable from <code>src</code>. A comment line is
424     one starting "#" or "//".
425     @deprecated Use rulesParserFromReader
426     */

427     public static String JavaDoc rulesStringFromReader( BufferedReader JavaDoc src ) {
428        try {
429            StringBuffer JavaDoc result = new StringBuffer JavaDoc();
430            String JavaDoc line;
431            while ((line = src.readLine()) != null) {
432                if (line.startsWith( "#" ) || line.startsWith( "//" )) continue; // Skip comment lines
433
result.append( line );
434                result.append( "\n" );
435            }
436            return result.toString();
437        }
438        catch (IOException JavaDoc e)
439            { throw new WrappedIOException( e ); }
440    }
441     
442     /**
443      * Processes the source reader stripping off comment lines and noting prefix
444      * definitions (@prefix) and rule inclusion commands (@include).
445      * Returns a parser which is bound to the stripped source text with
446      * associated prefix and rule inclusion definitions.
447     */

448     public static Parser rulesParserFromReader( BufferedReader JavaDoc src ) {
449        try {
450            StringBuffer JavaDoc result = new StringBuffer JavaDoc();
451            String JavaDoc line;
452            Map prefixes = new HashMap();
453            List preloadedRules = new ArrayList();
454            while ((line = src.readLine()) != null) {
455                if (line.startsWith("#")) continue; // Skip comment lines
456
line = line.trim();
457                if (line.startsWith("//")) continue; // Skip comment lines
458
if (line.startsWith("@prefix")) {
459                    line = line.substring("@prefix".length());
460                    String JavaDoc prefix = nextArg(line);
461                    String JavaDoc rest = nextAfterArg(line);
462                    if (prefix.endsWith(":")) prefix = prefix.substring(0, prefix.length() - 1);
463                    String JavaDoc url = extractURI(rest);
464                    prefixes.put(prefix, url);
465
466                } else if (line.startsWith("@include")) {
467                    // Include referenced rule file, either URL or local special case
468
line = line.substring("@include".length());
469                    String JavaDoc url = extractURI(line);
470                    // Check for predefined cases
471
if (url.equalsIgnoreCase("rdfs")) {
472                        preloadedRules.addAll( RDFSFBRuleReasoner.loadRules() );
473                        
474                    } else if (url.equalsIgnoreCase("owl")) {
475                        preloadedRules.addAll( OWLFBRuleReasoner.loadRules() ) ;
476                        
477                    } else if (url.equalsIgnoreCase("owlmicro")) {
478                        preloadedRules.addAll( OWLMicroReasoner.loadRules() ) ;
479                        
480                    } else if (url.equalsIgnoreCase("owlmini")) {
481                        preloadedRules.addAll( OWLMiniReasoner.loadRules() ) ;
482                        
483                    } else {
484                        // Just try loading as a URL
485
preloadedRules.addAll( rulesFromURL(url) );
486                    }
487
488                } else {
489                    result.append(line);
490                    result.append("\n");
491                }
492            }
493            Parser parser = new Parser(result.toString());
494            parser.registerPrefixMap(prefixes);
495            parser.addRulesPreload(preloadedRules);
496            return parser;
497        }
498        catch (IOException JavaDoc e)
499            { throw new WrappedIOException( e ); }
500    }
501
502     /**
503      * Helper function find a URI argument in the current string,
504      * optionally surrounded by matching <>.
505      */

506     private static String JavaDoc extractURI(String JavaDoc lineSoFar) {
507         String JavaDoc token = lineSoFar.trim();
508         if (token.startsWith("<")) {
509             int split = token.indexOf('>');
510             token = token.substring(1, split);
511         }
512         return token;
513     }
514
515     /**
516      * Helper function to return the next whitespace delimited argument
517      * from the string
518      */

519     private static String JavaDoc nextArg(String JavaDoc token) {
520         int start = nextSplit(0, false, token);
521         int stop = nextSplit(start, true, token);
522         return token.substring(start, stop);
523     }
524     
525     /**
526      * Helper function to return the remainder of the line after
527      * stripping off the next whitespace delimited argument
528      * from the string
529      */

530     private static String JavaDoc nextAfterArg(String JavaDoc token) {
531         int start = nextSplit(0, false, token);
532         int stop = nextSplit(start, true, token);
533         int rest = nextSplit(stop, false, token);
534         return token.substring(rest);
535     }
536     
537     /**
538      * Helper function - find index of next whitespace or non white
539      * after the start index.
540      */

541     private static int nextSplit(int start, boolean white, String JavaDoc line) {
542         int i = start;
543         while (i < line.length()) {
544             boolean isWhite = Character.isWhitespace(line.charAt(i));
545             if ((white & isWhite) || (!white & !isWhite)) {
546                 return i;
547             }
548             i++;
549         }
550         return i;
551     }
552
553     public static void main(String JavaDoc[] args) {
554         String JavaDoc test = " <http://myuri/fool>.";
555         String JavaDoc arg = nextArg(test);
556         String JavaDoc rest = nextAfterArg(test);
557         String JavaDoc uri = extractURI(rest);
558         System.out.println("ARG = [" + arg + "], URI = [" + uri + "]");
559     }
560     /**
561      * Run a pre-bound rule parser to extract it's rules
562      * @return a list of rules
563      * @throws ParserException if there is a problem
564      */

565     public static List parseRules(Parser parser) throws ParserException {
566         boolean finished = false;
567         List ruleset = new ArrayList();
568         ruleset.addAll(parser.getRulesPreload());
569         while (!finished) {
570             try {
571                 parser.peekToken();
572             } catch (NoSuchElementException e) {
573                 finished = true;
574                 break;
575             }
576             Rule rule = parser.parseRule();
577             ruleset.add(rule);
578         }
579         return ruleset;
580     }
581
582     /**
583      * Parse a string as a list a rules.
584      * @return a list of rules
585      * @throws ParserException if there is a problem
586      */

587     public static List parseRules(String JavaDoc source) throws ParserException {
588         return parseRules(new Parser(source));
589     }
590     
591
592 //=======================================================================
593
// parser support
594

595     /**
596      * Inner class which provides minimalist parsing support based on
597      * tokenisation with depth 1 lookahead. No sensible error reporting on offer.
598      * No embedded spaces supported.
599      */

600     public static class Parser {
601         
602         /** Tokenizer */
603         private Tokenizer stream;
604         
605         /** Look ahead, null if none */
606         private String JavaDoc lookahead;
607         
608         /** Trace back of recent tokens for error reporting */
609         protected List priorTokens = new ArrayList();
610         
611         /** Maximum number of recent tokens to remember */
612         private static final int maxPriors = 20;
613         
614         /** Variable table */
615         private Map varMap;
616         
617         /** Local prefix map */
618         private PrefixMapping prefixMapping = PrefixMapping.Factory.create();
619         
620         /** Pre-included rules */
621         private List preloadedRules = new ArrayList();
622         
623         /**
624          * Constructor
625          * @param source the string to be parsed
626          */

627         Parser(String JavaDoc source) {
628             stream = new Tokenizer(source, "()[], \t\n\r", "'", true);
629             lookahead = null;
630         }
631         
632         /**
633          * Register a new namespace prefix with the parser
634          */

635         void registerPrefix(String JavaDoc prefix, String JavaDoc namespace ) {
636             prefixMapping.setNsPrefix(prefix, namespace);
637         }
638         
639         /**
640          * Register a set of prefix to namespace mappings with the parser
641          */

642         void registerPrefixMap(Map map) {
643             prefixMapping.setNsPrefixes(map);
644         }
645         
646         /**
647          * Return a map of all the discovered prefixes
648          */

649         public Map getPrefixMap() {
650             return prefixMapping.getNsPrefixMap();
651         }
652         
653         /**
654          * Add a new set of preloaded rules.
655          */

656         void addRulesPreload(List rules) {
657             preloadedRules.addAll(rules);
658         }
659         
660         /**
661          * Return the complete set of preloaded rules;
662          */

663         public List getRulesPreload() {
664             return preloadedRules;
665         }
666         
667         /**
668          * Return the next token
669          */

670         String JavaDoc nextToken() {
671             if (lookahead != null) {
672                 String JavaDoc temp = lookahead;
673                 lookahead = null;
674                 return temp;
675             } else {
676                 String JavaDoc token = stream.nextToken();
677                 while (isSeparator(token)) {
678                     token = stream.nextToken();
679                 }
680                 priorTokens.add(0, token);
681                 if (priorTokens.size() > maxPriors) {
682                     priorTokens.remove(priorTokens.size()-1);
683                 }
684                 return token;
685             }
686         }
687         
688         /**
689          * Return a trace of the recently seen tokens, for use
690          * in error reporting
691          */

692         public String JavaDoc recentTokens() {
693             StringBuffer JavaDoc trace = new StringBuffer JavaDoc();
694             for (int i = priorTokens.size()-1; i >= 0; i--) {
695                 trace.append(priorTokens.get(i));
696                 trace.append(" ");
697             }
698             return trace.toString();
699         }
700         
701         /**
702          * Peek ahead one token.
703          */

704         String JavaDoc peekToken() {
705             if (lookahead == null) {
706                 lookahead = nextToken();
707             }
708             return lookahead;
709         }
710         
711         /**
712          * Push back a previously fetched token. Only depth 1 supported.
713          */

714         void pushback(String JavaDoc token) {
715             lookahead = token;
716         }
717         
718         /**
719          * Returns true if token is an skippable separator
720          */

721         boolean isSeparator(String JavaDoc token) {
722             if (token.length() == 1) {
723                 char c = token.charAt(0);
724                 return (c == ',' || Character.isWhitespace(c));
725             }
726             return false;
727         }
728         
729         /**
730          * Returns true if token is a syntax element ()[]
731          */

732         boolean isSyntax(String JavaDoc token) {
733             if (token.length() == 1) {
734                 char c = token.charAt(0);
735                 return (c == '(' || c == ')' || c == '[' || c == ']');
736             }
737             return false;
738         }
739         
740         /**
741          * Find the variable index for the given variable name
742          * and return a Node_RuleVariable with that index.
743          */

744         Node_RuleVariable getNodeVar(String JavaDoc name) {
745             Node_RuleVariable node = (Node_RuleVariable)varMap.get(name);
746             if (node == null) {
747                 node = new Node_RuleVariable(name, varMap.size());
748                 varMap.put(name, node);
749             }
750             return node;
751         }
752         
753         /**
754          * Translate a token to a node.
755          */

756         Node parseNode(String JavaDoc token) {
757             if (token.startsWith("?")) {
758                 return getNodeVar(token);
759                 // Dropped support for anon wildcards until the implementation is better resolved
760
} else if (token.equals("*") || token.equals("_")) {
761                 throw new ParserException("Wildcard variables no longer supported", this);
762 //// return Node_RuleVariable.ANY;
763
// return Node_RuleVariable.WILD;
764
} else if (token.indexOf(':') != -1) {
765                 String JavaDoc exp = prefixMapping.expandPrefix(token); // Local map first
766
exp = PrintUtil.expandQname(exp); // Retain global map for backward compatibility
767
if (exp == token) {
768                     // No expansion was possible
769
String JavaDoc prefix = token.substring(0, token.indexOf(':'));
770                     if (prefix.equals("http") || prefix.equals("urn")
771                      || prefix.equals("ftp") || prefix.equals("mailto")) {
772                         // assume it is all OK and fall through
773
} else {
774                         // Likely to be a typo in a qname or failure to register
775
throw new ParserException("Unrecognized qname prefix (" + prefix + ") in rule", this);
776                     }
777                 }
778                 return Node.createURI(exp);
779             } else if (peekToken().equals("(")) {
780                 Functor f = new Functor(token, parseNodeList(), BuiltinRegistry.theRegistry);
781                 return Functor.makeFunctorNode( f );
782             } else if (token.equals("'")) {
783                 // A plain literal
784
String JavaDoc lit = nextToken();
785                 // Skip the trailing quote
786
nextToken();
787                 return Node.createLiteral(lit, "", false);
788             } else if ( Character.isDigit(token.charAt(0)) ||
789                          (token.charAt(0) == '-' && token.length() > 1 && Character.isDigit(token.charAt(1))) ) {
790                 // A number literal
791
return parseNumber(token);
792             } else {
793                 // A uri
794
return Node.createURI(token);
795             }
796         }
797         
798         /**
799          * Turn a possible numeric token into typed literal else a plain literal
800          * @return the constructed literal node
801          */

802         Node parseNumber(String JavaDoc lit) {
803             if ( Character.isDigit(lit.charAt(0)) ||
804                 (lit.charAt(0) == '-' && lit.length() > 1 && Character.isDigit(lit.charAt(1))) ) {
805                 if (lit.indexOf(".") != -1) {
806                     // Float?
807
if (XSDDatatype.XSDfloat.isValid(lit)) {
808                         return Node.createLiteral(lit, "", XSDDatatype.XSDfloat);
809                     }
810                 } else {
811                     // Int?
812
if (XSDDatatype.XSDint.isValid(lit)) {
813                         return Node.createLiteral(lit, "", XSDDatatype.XSDint);
814                     }
815                 }
816             }
817             // Default is a plain literal
818
return Node.createLiteral(lit, "", false);
819         }
820         
821         /**
822          * Parse a list of nodes delimited by parentheses
823          */

824         List parseNodeList() {
825             String JavaDoc token = nextToken();
826             if (!token.equals("(")) {
827                 throw new ParserException("Expected '(' at start of clause, found " + token, this);
828             }
829             token = nextToken();
830             List nodeList = new ArrayList();
831             while (!isSyntax(token)) {
832                 nodeList.add(parseNode(token));
833                 token = nextToken();
834             }
835             if (!token.equals(")")) {
836                 throw new ParserException("Expected ')' at end of clause, found " + token, this);
837             }
838             return nodeList;
839         }
840         
841         /**
842          * Parse a clause, could be a triple pattern, a rule or a functor
843          */

844         Object JavaDoc parseClause() {
845             String JavaDoc token = peekToken();
846             if (token.equals("(")) {
847                 List nodes = parseNodeList();
848                 if (nodes.size() != 3) {
849                     throw new ParserException("Triple with " + nodes.size() + " nodes!", this);
850                 }
851                 return new TriplePattern((Node)nodes.get(0), (Node)nodes.get(1), (Node)nodes.get(2));
852             } else if (token.equals("[")) {
853                 nextToken();
854                 return doParseRule(true);
855             } else {
856                 String JavaDoc name = nextToken();
857                 List args = parseNodeList();
858                 Functor clause = new Functor(name, args, BuiltinRegistry.theRegistry);
859                 if (clause.getImplementor() == null) {
860                     // Not a fatal error becase later processing can add this
861
// implementation to the registry
862
logger.warn("Rule references unimplemented functor: " + name);
863                 }
864                 return clause;
865             }
866         }
867         
868         
869         /**
870          * Parse a rule, terminated by a "]" or "." character.
871          */

872         public Rule parseRule() {
873             return doParseRule(false);
874         }
875         
876         /**
877          * Parse a rule, terminated by a "]" or "." character.
878          * @param retainVarMap set to true to ccause the existing varMap to be left in place, which
879          * is required for nested rules.
880          */

881         private Rule doParseRule(boolean retainVarMap) {
882             try {
883                 // Skip initial '[' if present
884
if (peekToken().equals("[")) {
885                     nextToken();
886                 }
887                 // Check for optional name
888
String JavaDoc name = null;
889                 String JavaDoc token = peekToken();
890                 if (token.endsWith(":")) {
891                     name = token.substring(0, token.length()-1);
892                     nextToken();
893                 }
894                 // Start rule parsing with empty variable table
895
if (!retainVarMap) varMap = new HashMap();
896                 // Body
897
List body = new ArrayList();
898                 token = peekToken();
899                 while ( !(token.equals("->") || token.equals("<-")) ) {
900                     body.add(parseClause());
901                     token = peekToken();
902                 }
903                 boolean backwardRule = token.equals("<-");
904                 List head = new ArrayList();
905                 token = nextToken(); // skip -> token
906
token = peekToken();
907                 while ( !(token.equals(".") || token.equals("]")) ) {
908                     head.add(parseClause());
909                     token = peekToken();
910                 }
911                 nextToken(); // consume the terminating token
912
Rule r = null;
913                 if (backwardRule) {
914                     r = new Rule(name, body, head);
915                 } else {
916                     r = new Rule(name, head, body);
917                 }
918                 r.numVars = varMap.keySet().size();
919                 r.isBackward = backwardRule;
920                 return r;
921             } catch (NoSuchElementException e) {
922                 throw new ParserException("Malformed rule", this);
923             }
924         }
925
926     }
927    
928     /** Equality override */
929     public boolean equals(Object JavaDoc o) {
930         // Pass 1 - just check basic shape
931
if (! (o instanceof Rule) ) return false;
932         Rule other = (Rule) o;
933         if (other.head.length != head.length) return false;
934         if (other.body.length != body.length) return false;
935         // Pass 2 - check clause by clause matching
936
for (int i = 0; i < body.length; i++) {
937             if (! ((ClauseEntry)body[i]).sameAs((ClauseEntry)other.body[i]) ) return false;
938         }
939         for (int i = 0; i < head.length; i++) {
940             if (! ((ClauseEntry)head[i]).sameAs((ClauseEntry)other.head[i]) ) return false;
941         }
942         return true;
943     }
944         
945     /** hash function override */
946     public int hashCode() {
947         int hash = 0;
948         for (int i = 0; i < body.length; i++) {
949             hash = (hash << 1) ^ body[i].hashCode();
950         }
951         for (int i = 0; i < head.length; i++) {
952             hash = (hash << 1) ^ head[i].hashCode();
953         }
954         return hash;
955     }
956     
957     /**
958      * Compare clause entries, taking into account variable indices.
959      * The equality function ignores differences between variables.
960      */

961     public boolean sameAs(Object JavaDoc o) {
962         return equals(o);
963     }
964     
965 //=======================================================================
966
// Other supporting inner classes
967

968     /**
969      * Inner class. Exception raised if there is a problem
970      * during rule parsing.
971      */

972     public static class ParserException extends JenaException {
973         
974         /** constructor */
975         public ParserException(String JavaDoc message, Parser parser) {
976             super(constructMessage(message, parser));
977         }
978         
979         /**
980          * Extract context trace from prior tokens stack
981          */

982         private static String JavaDoc constructMessage(String JavaDoc baseMessage, Parser parser) {
983             StringBuffer JavaDoc message = new StringBuffer JavaDoc();
984             message.append(baseMessage);
985             message.append("\nAt '");
986             message.append(parser.recentTokens());
987             message.append("'");
988             return message.toString();
989         }
990         
991     }
992     
993 }
994
995 /*
996     (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
997     All rights reserved.
998
999     Redistribution and use in source and binary forms, with or without
1000    modification, are permitted provided that the following conditions
1001    are met:
1002
1003    1. Redistributions of source code must retain the above copyright
1004       notice, this list of conditions and the following disclaimer.
1005
1006    2. Redistributions in binary form must reproduce the above copyright
1007       notice, this list of conditions and the following disclaimer in the
1008       documentation and/or other materials provided with the distribution.
1009
1010    3. The name of the author may not be used to endorse or promote products
1011       derived from this software without specific prior written permission.
1012
1013    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1014    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1015    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1016    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1017    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1018    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1019    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1020    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1021    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1022    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1023*/

1024
Popular Tags