1 10 package com.hp.hpl.jena.reasoner.rulesys; 11 12 import java.io.BufferedReader ; 13 import java.io.IOException ; 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 64 public class Rule implements ClauseEntry { 65 66 69 70 protected ClauseEntry[] body; 71 72 73 protected ClauseEntry[] head; 74 75 76 protected String name; 77 78 79 protected int numVars = -1; 80 81 82 protected boolean isBackward = false; 83 84 static Log logger = LogFactory.getLog(Rule.class); 85 86 91 public Rule(List head, List body) { 92 this(null, head, body); 93 } 94 95 101 public Rule(String 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 113 public Rule(String name, ClauseEntry[] head, ClauseEntry[] body) { 114 this.name = name; 115 this.head = head; 116 this.body = body; 117 } 118 119 122 125 public int bodyLength() { 126 return body.length; 127 } 128 129 132 public ClauseEntry getBodyElement(int n) { 133 return body[n]; 134 } 135 136 139 public ClauseEntry[] getBody() { 140 return body; 141 } 142 143 144 147 public int headLength() { 148 return head.length; 149 } 150 151 154 public ClauseEntry getHeadElement(int n) { 155 return head[n]; 156 } 157 158 161 public ClauseEntry[] getHead() { 162 return head; 163 } 164 165 168 public boolean isBackward() { 169 return isBackward; 170 } 171 172 176 public void setBackward(boolean flag) { 177 isBackward = flag; 178 } 179 180 183 public String getName() { 184 return name; 185 } 186 187 191 public void setNumVars(int n) { 192 numVars = n; 193 } 194 195 199 public int getNumVars() { 200 if (numVars == -1) { 201 int max = findVars(body, -1); 204 max = findVars(head, max); 205 numVars = max + 1; 206 } 207 return numVars; 208 } 209 210 213 private int findVars(Object [] nodes, int maxIn) { 214 int max = maxIn; 215 for (int i = 0; i < nodes.length; i++) { 216 Object 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 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 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 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 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 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 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 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 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 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 352 public String toString() { 353 StringBuffer buff = new StringBuffer (); 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 388 public String toShortString() { 389 if (name != null) { 390 return name; 391 } else { 392 return toString(); 393 } 394 } 395 396 399 403 public static Rule parseRule(String source) throws ParserException { 404 Parser parser = new Parser(source); 405 return parser.parseRule(); 406 } 407 408 412 public static List rulesFromURL( String uri ) { 413 try { 414 BufferedReader br = FileUtils.readerFromURL( uri ); 415 return parseRules( Rule.rulesParserFromReader( br ) ); 416 } 417 catch (WrappedIOException e) 418 { throw new RulesetNotFoundException( uri ); } 419 } 420 421 427 public static String rulesStringFromReader( BufferedReader src ) { 428 try { 429 StringBuffer result = new StringBuffer (); 430 String line; 431 while ((line = src.readLine()) != null) { 432 if (line.startsWith( "#" ) || line.startsWith( "//" )) continue; result.append( line ); 434 result.append( "\n" ); 435 } 436 return result.toString(); 437 } 438 catch (IOException e) 439 { throw new WrappedIOException( e ); } 440 } 441 442 448 public static Parser rulesParserFromReader( BufferedReader src ) { 449 try { 450 StringBuffer result = new StringBuffer (); 451 String line; 452 Map prefixes = new HashMap(); 453 List preloadedRules = new ArrayList(); 454 while ((line = src.readLine()) != null) { 455 if (line.startsWith("#")) continue; line = line.trim(); 457 if (line.startsWith("//")) continue; if (line.startsWith("@prefix")) { 459 line = line.substring("@prefix".length()); 460 String prefix = nextArg(line); 461 String rest = nextAfterArg(line); 462 if (prefix.endsWith(":")) prefix = prefix.substring(0, prefix.length() - 1); 463 String url = extractURI(rest); 464 prefixes.put(prefix, url); 465 466 } else if (line.startsWith("@include")) { 467 line = line.substring("@include".length()); 469 String url = extractURI(line); 470 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 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 e) 499 { throw new WrappedIOException( e ); } 500 } 501 502 506 private static String extractURI(String lineSoFar) { 507 String 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 519 private static String nextArg(String token) { 520 int start = nextSplit(0, false, token); 521 int stop = nextSplit(start, true, token); 522 return token.substring(start, stop); 523 } 524 525 530 private static String nextAfterArg(String 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 541 private static int nextSplit(int start, boolean white, String 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 [] args) { 554 String test = " <http://myuri/fool>."; 555 String arg = nextArg(test); 556 String rest = nextAfterArg(test); 557 String uri = extractURI(rest); 558 System.out.println("ARG = [" + arg + "], URI = [" + uri + "]"); 559 } 560 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 587 public static List parseRules(String source) throws ParserException { 588 return parseRules(new Parser(source)); 589 } 590 591 592 595 600 public static class Parser { 601 602 603 private Tokenizer stream; 604 605 606 private String lookahead; 607 608 609 protected List priorTokens = new ArrayList(); 610 611 612 private static final int maxPriors = 20; 613 614 615 private Map varMap; 616 617 618 private PrefixMapping prefixMapping = PrefixMapping.Factory.create(); 619 620 621 private List preloadedRules = new ArrayList(); 622 623 627 Parser(String source) { 628 stream = new Tokenizer(source, "()[], \t\n\r", "'", true); 629 lookahead = null; 630 } 631 632 635 void registerPrefix(String prefix, String namespace ) { 636 prefixMapping.setNsPrefix(prefix, namespace); 637 } 638 639 642 void registerPrefixMap(Map map) { 643 prefixMapping.setNsPrefixes(map); 644 } 645 646 649 public Map getPrefixMap() { 650 return prefixMapping.getNsPrefixMap(); 651 } 652 653 656 void addRulesPreload(List rules) { 657 preloadedRules.addAll(rules); 658 } 659 660 663 public List getRulesPreload() { 664 return preloadedRules; 665 } 666 667 670 String nextToken() { 671 if (lookahead != null) { 672 String temp = lookahead; 673 lookahead = null; 674 return temp; 675 } else { 676 String 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 692 public String recentTokens() { 693 StringBuffer trace = new StringBuffer (); 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 704 String peekToken() { 705 if (lookahead == null) { 706 lookahead = nextToken(); 707 } 708 return lookahead; 709 } 710 711 714 void pushback(String token) { 715 lookahead = token; 716 } 717 718 721 boolean isSeparator(String 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 732 boolean isSyntax(String 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 744 Node_RuleVariable getNodeVar(String 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 756 Node parseNode(String token) { 757 if (token.startsWith("?")) { 758 return getNodeVar(token); 759 } else if (token.equals("*") || token.equals("_")) { 761 throw new ParserException("Wildcard variables no longer supported", this); 762 } else if (token.indexOf(':') != -1) { 765 String exp = prefixMapping.expandPrefix(token); exp = PrintUtil.expandQname(exp); if (exp == token) { 768 String prefix = token.substring(0, token.indexOf(':')); 770 if (prefix.equals("http") || prefix.equals("urn") 771 || prefix.equals("ftp") || prefix.equals("mailto")) { 772 } else { 774 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 String lit = nextToken(); 785 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 return parseNumber(token); 792 } else { 793 return Node.createURI(token); 795 } 796 } 797 798 802 Node parseNumber(String 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 if (XSDDatatype.XSDfloat.isValid(lit)) { 808 return Node.createLiteral(lit, "", XSDDatatype.XSDfloat); 809 } 810 } else { 811 if (XSDDatatype.XSDint.isValid(lit)) { 813 return Node.createLiteral(lit, "", XSDDatatype.XSDint); 814 } 815 } 816 } 817 return Node.createLiteral(lit, "", false); 819 } 820 821 824 List parseNodeList() { 825 String 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 844 Object parseClause() { 845 String 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 name = nextToken(); 857 List args = parseNodeList(); 858 Functor clause = new Functor(name, args, BuiltinRegistry.theRegistry); 859 if (clause.getImplementor() == null) { 860 logger.warn("Rule references unimplemented functor: " + name); 863 } 864 return clause; 865 } 866 } 867 868 869 872 public Rule parseRule() { 873 return doParseRule(false); 874 } 875 876 881 private Rule doParseRule(boolean retainVarMap) { 882 try { 883 if (peekToken().equals("[")) { 885 nextToken(); 886 } 887 String name = null; 889 String token = peekToken(); 890 if (token.endsWith(":")) { 891 name = token.substring(0, token.length()-1); 892 nextToken(); 893 } 894 if (!retainVarMap) varMap = new HashMap(); 896 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(); token = peekToken(); 907 while ( !(token.equals(".") || token.equals("]")) ) { 908 head.add(parseClause()); 909 token = peekToken(); 910 } 911 nextToken(); 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 929 public boolean equals(Object o) { 930 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 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 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 961 public boolean sameAs(Object o) { 962 return equals(o); 963 } 964 965 968 972 public static class ParserException extends JenaException { 973 974 975 public ParserException(String message, Parser parser) { 976 super(constructMessage(message, parser)); 977 } 978 979 982 private static String constructMessage(String baseMessage, Parser parser) { 983 StringBuffer message = new StringBuffer (); 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 1024 | Popular Tags |